summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrivahni Chivukula <srivahni.chivukula@intel.com>2020-02-14 05:11:08 -0800
committerSrivahni Chivukula <srivahni.chivukula@intel.com>2020-02-20 03:41:00 -0800
commitb11b37f11fa45ab149e8a88a183b70f077c0f48e (patch)
treea9e0d2c0c65d7d822a736caf4e093f7df2f5aed3
parent38df1b0ee0f1d6cd3bf11f94adf7c952f32c191c (diff)
Add Composite Application API
Implemented Composite application API and added create, get and delete handlers for the composite applications. Formatted Project related .go files Issue-ID: MULTICLOUD-994 Signed-off-by: Srivahni Chivukula <srivahni.chivukula@intel.com> Change-Id: I7cef1a2c75f8cb39f397dcbb3f5d7bb2a57b4a72
-rw-r--r--src/orchestrator/api/api.go20
-rw-r--r--src/orchestrator/api/composite_app_handler.go113
-rw-r--r--src/orchestrator/api/projecthandler_test.go50
-rw-r--r--src/orchestrator/cmd/main.go2
-rw-r--r--src/orchestrator/pkg/module/compositeapp.go158
-rw-r--r--src/orchestrator/pkg/module/module.go19
-rw-r--r--src/orchestrator/pkg/module/project.go22
-rw-r--r--src/orchestrator/pkg/module/project_test.go31
8 files changed, 345 insertions, 70 deletions
diff --git a/src/orchestrator/api/api.go b/src/orchestrator/api/api.go
index e37b158a..1cb4299e 100644
--- a/src/orchestrator/api/api.go
+++ b/src/orchestrator/api/api.go
@@ -1,5 +1,5 @@
/*
-Copyright 2018 Intel Corporation.
+Copyright 2020 Intel Corporation.
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
@@ -10,6 +10,7 @@ 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 api
import (
@@ -17,9 +18,11 @@ import (
"github.com/gorilla/mux"
)
+
var moduleClient *moduleLib.Client
+
// NewRouter creates a router that registers the various urls that are supported
-func NewRouter(projectClient moduleLib.ProjectManager) *mux.Router {
+func NewRouter(projectClient moduleLib.ProjectManager, compositeAppClient moduleLib.CompositeAppManager) *mux.Router {
router := mux.NewRouter().PathPrefix("/v2").Subrouter()
moduleClient = moduleLib.NewClient()
@@ -33,5 +36,16 @@ func NewRouter(projectClient moduleLib.ProjectManager) *mux.Router {
router.HandleFunc("/projects/{project-name}", projHandler.getHandler).Methods("GET")
router.HandleFunc("/projects/{project-name}", projHandler.deleteHandler).Methods("DELETE")
+ if compositeAppClient == nil {
+ compositeAppClient = moduleClient.CompositeApp
+ }
+ compAppHandler := compositeAppHandler{
+ client: compositeAppClient,
+ }
+
+ router.HandleFunc("/projects/{project-name}/composite-apps", compAppHandler.createHandler).Methods("POST")
+ router.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}", compAppHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}", compAppHandler.deleteHandler).Methods("DELETE")
+
return router
-} \ No newline at end of file
+}
diff --git a/src/orchestrator/api/composite_app_handler.go b/src/orchestrator/api/composite_app_handler.go
new file mode 100644
index 00000000..42c72cdb
--- /dev/null
+++ b/src/orchestrator/api/composite_app_handler.go
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 api
+
+import (
+ "encoding/json"
+ "io"
+ "net/http"
+
+ moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+
+ "github.com/gorilla/mux"
+)
+
+// compositeAppHandler to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type compositeAppHandler struct {
+ // Interface that implements CompositeApp operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.CompositeAppManager
+}
+
+// createHandler handles creation of the CompositeApp entry in the database
+// This is a multipart handler
+func (h compositeAppHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+ var c moduleLib.CompositeApp
+
+ err := json.NewDecoder(r.Body).Decode(&c)
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if c.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ vars := mux.Vars(r)
+ projectName := vars["project-name"]
+
+ ret, err := h.client.CreateCompositeApp(c, projectName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// getHandler handles GET operations on a particular CompositeApp Name
+// Returns a compositeApp
+func (h compositeAppHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["composite-app-name"]
+ version := vars["version"]
+ projectName := vars["project-name"]
+
+ ret, err := h.client.GetCompositeApp(name, version, projectName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// deleteHandler handles DELETE operations on a particular CompositeApp Name
+func (h compositeAppHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["composite-app-name"]
+ version := vars["version"]
+ projectName := vars["project-name"]
+
+ err := h.client.DeleteCompositeApp(name, version, projectName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/orchestrator/api/projecthandler_test.go b/src/orchestrator/api/projecthandler_test.go
index ee6ed358..c76764b3 100644
--- a/src/orchestrator/api/projecthandler_test.go
+++ b/src/orchestrator/api/projecthandler_test.go
@@ -86,23 +86,23 @@ func TestProjectCreateHandler(t *testing.T) {
}`)),
expected: moduleLib.Project{
MetaData: moduleLib.ProjectMetaData{
- Name: "testProject",
+ Name: "testProject",
Description: "Test Project used for unit testing",
- UserData1: "data1",
- UserData2: "data2",
+ UserData1: "data1",
+ UserData2: "data2",
},
},
projectClient: &mockProjectManager{
//Items that will be returned by the mocked Client
Items: []moduleLib.Project{
- moduleLib.Project{
- MetaData: moduleLib.ProjectMetaData{
- Name: "testProject",
- Description: "Test Project used for unit testing",
- UserData1: "data1",
- UserData2: "data2",
- },
- },
+ moduleLib.Project{
+ MetaData: moduleLib.ProjectMetaData{
+ Name: "testProject",
+ Description: "Test Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
+ },
},
},
},
@@ -119,7 +119,7 @@ func TestProjectCreateHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
request := httptest.NewRequest("POST", "/v2/projects", testCase.reader)
- resp := executeRequest(request, NewRouter(testCase.projectClient))
+ resp := executeRequest(request, NewRouter(testCase.projectClient, nil))
//Check returned code
if resp.StatusCode != testCase.expectedCode {
@@ -154,23 +154,23 @@ func TestProjectGetHandler(t *testing.T) {
expectedCode: http.StatusOK,
expected: moduleLib.Project{
MetaData: moduleLib.ProjectMetaData{
- Name: "testProject",
+ Name: "testProject",
Description: "Test Project used for unit testing",
- UserData1: "data1",
- UserData2: "data2",
+ UserData1: "data1",
+ UserData2: "data2",
},
},
name: "testProject",
projectClient: &mockProjectManager{
Items: []moduleLib.Project{
- moduleLib.Project{
- MetaData: moduleLib.ProjectMetaData{
- Name: "testProject",
- Description: "Test Project used for unit testing",
- UserData1: "data1",
- UserData2: "data2",
- },
- },
+ moduleLib.Project{
+ MetaData: moduleLib.ProjectMetaData{
+ Name: "testProject",
+ Description: "Test Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
+ },
},
},
},
@@ -188,7 +188,7 @@ func TestProjectGetHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
request := httptest.NewRequest("GET", "/v2/projects/"+testCase.name, nil)
- resp := executeRequest(request, NewRouter(testCase.projectClient))
+ resp := executeRequest(request, NewRouter(testCase.projectClient, nil))
//Check returned code
if resp.StatusCode != testCase.expectedCode {
@@ -237,7 +237,7 @@ func TestProjectDeleteHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
request := httptest.NewRequest("DELETE", "/v2/projects/"+testCase.name, nil)
- resp := executeRequest(request, NewRouter(testCase.projectClient))
+ resp := executeRequest(request, NewRouter(testCase.projectClient, nil))
//Check returned code
if resp.StatusCode != testCase.expectedCode {
diff --git a/src/orchestrator/cmd/main.go b/src/orchestrator/cmd/main.go
index f46fe910..087caba3 100644
--- a/src/orchestrator/cmd/main.go
+++ b/src/orchestrator/cmd/main.go
@@ -47,7 +47,7 @@ func main() {
log.Fatalln("Exiting...")
}
- httpRouter := api.NewRouter(nil)
+ httpRouter := api.NewRouter(nil, nil)
loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter)
log.Println("Starting Kubernetes Multicloud API")
diff --git a/src/orchestrator/pkg/module/compositeapp.go b/src/orchestrator/pkg/module/compositeapp.go
new file mode 100644
index 00000000..0a4e158c
--- /dev/null
+++ b/src/orchestrator/pkg/module/compositeapp.go
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 governinog permissions and
+ * limitations under the License.
+ */
+
+package module
+
+import (
+ "encoding/json"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+// CompositeApp contains metadata and spec for CompositeApps
+type CompositeApp struct {
+ Metadata CompositeAppMetaData `json:"metadata"`
+ Spec CompositeAppSpec `json:"spec"`
+}
+
+//CompositeAppMetaData contains the parameters needed for CompositeApps
+type CompositeAppMetaData struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ UserData1 string `userData1:"userData1"`
+ UserData2 string `userData2:"userData2"`
+}
+
+//CompositeAppSpec contains the Version of the CompositeApp
+type CompositeAppSpec struct {
+ Version string `json:"version"`
+}
+
+// CompositeAppKey is the key structure that is used in the database
+type CompositeAppKey struct {
+ CompositeAppName string `json:"compositeappname"`
+ Version string `json:"version"`
+ Project string `json:"project"`
+}
+
+// We will use json marshalling to convert to string to
+// preserve the underlying structure.
+func (cK CompositeAppKey) String() string {
+ out, err := json.Marshal(cK)
+ if err != nil {
+ return ""
+ }
+ return string(out)
+}
+
+// CompositeAppManager is an interface exposes the CompositeApp functionality
+type CompositeAppManager interface {
+ CreateCompositeApp(c CompositeApp, p string) (CompositeApp, error)
+ GetCompositeApp(name string, version string, p string) (CompositeApp, error)
+ DeleteCompositeApp(name string, version string, p string) error
+}
+
+// CompositeAppClient implements the CompositeAppManager
+// It will also be used to maintain some localized state
+type CompositeAppClient struct {
+ storeName string
+ tagMeta, tagContent string
+}
+
+// NewCompositeAppClient returns an instance of the CompositeAppClient
+// which implements the CompositeAppManager
+func NewCompositeAppClient() *CompositeAppClient {
+ return &CompositeAppClient{
+ storeName: "orchestrator",
+ tagMeta: "compositeAppmetadata",
+ }
+}
+
+// CreateCompositeApp creates a new collection based on the CompositeApp
+func (v *CompositeAppClient) CreateCompositeApp(c CompositeApp, p string) (CompositeApp, error) {
+
+ //Construct the composite key to select the entry
+ key := CompositeAppKey{
+ CompositeAppName: c.Metadata.Name,
+ Version: c.Spec.Version,
+ Project: p,
+ }
+
+ //Check if this CompositeApp already exists
+ _, err := v.GetCompositeApp(c.Metadata.Name, c.Spec.Version, p)
+ if err == nil {
+ return CompositeApp{}, pkgerrors.New("CompositeApp already exists")
+ }
+
+ //Check if Project exists
+ _, err = NewProjectClient().GetProject(p)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.New("Unable to find the project")
+ }
+
+ err = db.DBconn.Create(v.storeName, key, v.tagMeta, c)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.Wrap(err, "Creating DB Entry")
+ }
+
+ return c, nil
+}
+
+// GetCompositeApp returns the CompositeApp for corresponding name
+func (v *CompositeAppClient) GetCompositeApp(name string, version string, p string) (CompositeApp, error) {
+
+ //Construct the composite key to select the entry
+ key := CompositeAppKey{
+ CompositeAppName: name,
+ Version: version,
+ Project: p,
+ }
+ value, err := db.DBconn.Read(v.storeName, key, v.tagMeta)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.Wrap(err, "Get composite application")
+ }
+
+ //value is a byte array
+ if value != nil {
+ compApp := CompositeApp{}
+ err = db.DBconn.Unmarshal(value, &compApp)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+ }
+ return compApp, nil
+ }
+
+ return CompositeApp{}, pkgerrors.New("Error getting composite application")
+}
+
+// DeleteCompositeApp deletes the CompositeApp from database
+func (v *CompositeAppClient) DeleteCompositeApp(name string, version string, p string) error {
+
+ //Construct the composite key to select the entry
+ key := CompositeAppKey{
+ CompositeAppName: name,
+ Version: version,
+ Project: p,
+ }
+ err := db.DBconn.Delete(v.storeName, key, v.tagMeta)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Delete CompositeApp Entry;")
+ }
+
+ return nil
+}
diff --git a/src/orchestrator/pkg/module/module.go b/src/orchestrator/pkg/module/module.go
index e4482098..d03e5ffb 100644
--- a/src/orchestrator/pkg/module/module.go
+++ b/src/orchestrator/pkg/module/module.go
@@ -16,19 +16,18 @@
package module
-import (
- )
-
// Client for using the services in the orchestrator
type Client struct {
- Project *ProjectClient
- // Add Clients for API's here
+ Project *ProjectClient
+ CompositeApp *CompositeAppClient
+ // Add Clients for API's here
}
// NewClient creates a new client for using the services
func NewClient() *Client {
- c:= &Client{}
- c.Project = NewProjectClient()
- // Add Client API handlers here
- return c
-} \ No newline at end of file
+ c := &Client{}
+ c.Project = NewProjectClient()
+ c.CompositeApp = NewCompositeAppClient()
+ // Add Client API handlers here
+ return c
+}
diff --git a/src/orchestrator/pkg/module/project.go b/src/orchestrator/pkg/module/project.go
index 796e9e99..a95251b5 100644
--- a/src/orchestrator/pkg/module/project.go
+++ b/src/orchestrator/pkg/module/project.go
@@ -18,27 +18,25 @@ package module
import (
"encoding/json"
+
"github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
pkgerrors "github.com/pkg/errors"
)
-
// Project contains the metaData for Projects
type Project struct {
- MetaData ProjectMetaData`json:"metadata"`
+ MetaData ProjectMetaData `json:"metadata"`
}
-
// ProjectMetaData contains the parameters for creating a project
type ProjectMetaData struct {
- Name string `json:"name"`
+ Name string `json:"name"`
Description string `json:"description"`
- UserData1 string `userData1:"userData1"`
- UserData2 string `userData2:"userData2"`
+ UserData1 string `userData1:"userData1"`
+ UserData2 string `userData2:"userData2"`
}
-
// ProjectKey is the key structure that is used in the database
type ProjectKey struct {
ProjectName string `json:"project"`
@@ -55,7 +53,6 @@ func (pk ProjectKey) String() string {
return string(out)
}
-
// ProjectManager is an interface exposes the Project functionality
type ProjectManager interface {
CreateProject(pr Project) (Project, error)
@@ -63,7 +60,6 @@ type ProjectManager interface {
DeleteProject(name string) error
}
-
// ProjectClient implements the ProjectManager
// It will also be used to maintain some localized state
type ProjectClient struct {
@@ -71,17 +67,15 @@ type ProjectClient struct {
tagMeta, tagContent string
}
-
// NewProjectClient returns an instance of the ProjectClient
// which implements the ProjectManager
func NewProjectClient() *ProjectClient {
return &ProjectClient{
storeName: "orchestrator",
- tagMeta: "projectmetadata",
+ tagMeta: "projectmetadata",
}
}
-
// CreateProject a new collection based on the project
func (v *ProjectClient) CreateProject(p Project) (Project, error) {
@@ -104,7 +98,6 @@ func (v *ProjectClient) CreateProject(p Project) (Project, error) {
return p, nil
}
-
// GetProject returns the Project for corresponding name
func (v *ProjectClient) GetProject(name string) (Project, error) {
@@ -130,7 +123,6 @@ func (v *ProjectClient) GetProject(name string) (Project, error) {
return Project{}, pkgerrors.New("Error getting Project")
}
-
// DeleteProject the Project from database
func (v *ProjectClient) DeleteProject(name string) error {
@@ -145,4 +137,4 @@ func (v *ProjectClient) DeleteProject(name string) error {
//TODO: Delete the collection when the project is deleted
return nil
-} \ No newline at end of file
+}
diff --git a/src/orchestrator/pkg/module/project_test.go b/src/orchestrator/pkg/module/project_test.go
index 90fe30bc..f6856f86 100644
--- a/src/orchestrator/pkg/module/project_test.go
+++ b/src/orchestrator/pkg/module/project_test.go
@@ -23,7 +23,6 @@ import (
"github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
-
pkgerrors "github.com/pkg/errors"
)
@@ -39,18 +38,18 @@ func TestCreateProject(t *testing.T) {
label: "Create Project",
inp: Project{
MetaData: ProjectMetaData{
- Name: "testProject",
+ Name: "testProject",
Description: "A sample Project used for unit testing",
- UserData1: "data1",
- UserData2: "data2",
+ UserData1: "data1",
+ UserData2: "data2",
},
},
expected: Project{
MetaData: ProjectMetaData{
- Name:"testProject",
+ Name: "testProject",
Description: "A sample Project used for unit testing",
- UserData1: "data1",
- UserData2: "data2",
+ UserData1: "data1",
+ UserData2: "data2",
},
},
expectedError: "",
@@ -102,10 +101,10 @@ func TestGetProject(t *testing.T) {
name: "testProject",
expected: Project{
MetaData: ProjectMetaData{
- Name: "testProject",
+ Name: "testProject",
Description: "Test project for unit testing",
- UserData1: "userData1",
- UserData2: "userData2",
+ UserData1: "userData1",
+ UserData2: "userData2",
},
},
expectedError: "",
@@ -114,12 +113,12 @@ func TestGetProject(t *testing.T) {
ProjectKey{ProjectName: "testProject"}.String(): {
"projectmetadata": []byte(
"{" +
- "\"metadata\" : {"+
- "\"Name\":\"testProject\"," +
- "\"Description\":\"Test project for unit testing\"," +
- "\"UserData1\": \"userData1\","+
- "\"UserData2\":\"userData2\"}"+
- "}"),
+ "\"metadata\" : {" +
+ "\"Name\":\"testProject\"," +
+ "\"Description\":\"Test project for unit testing\"," +
+ "\"UserData1\": \"userData1\"," +
+ "\"UserData2\":\"userData2\"}" +
+ "}"),
},
},
},