diff options
Diffstat (limited to 'src/orchestrator/pkg/module')
-rw-r--r-- | src/orchestrator/pkg/module/app.go | 202 | ||||
-rw-r--r-- | src/orchestrator/pkg/module/app_test.go | 327 | ||||
-rw-r--r-- | src/orchestrator/pkg/module/compositeapp.go | 8 | ||||
-rw-r--r-- | src/orchestrator/pkg/module/module.go | 2 |
4 files changed, 535 insertions, 4 deletions
diff --git a/src/orchestrator/pkg/module/app.go b/src/orchestrator/pkg/module/app.go new file mode 100644 index 00000000..c25a1b51 --- /dev/null +++ b/src/orchestrator/pkg/module/app.go @@ -0,0 +1,202 @@ +/* + * 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" +) + +// App contains metadata for Apps +type App struct { + Metadata AppMetaData `json:"metadata"` +} + +//AppMetaData contains the parameters needed for Apps +type AppMetaData struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +//AppContent contains fileContent +type AppContent struct { + FileContent string +} + +// AppKey is the key structure that is used in the database +type AppKey struct { + App string `json:"app"` + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + CompositeAppVersion string `json:"compositeappversion"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (aK AppKey) String() string { + out, err := json.Marshal(aK) + if err != nil { + return "" + } + return string(out) +} + +// AppManager is an interface exposes the App functionality +type AppManager interface { + CreateApp(a App, ac AppContent, p string, cN string, cV string) (App, error) + GetApp(name string, p string, cN string, cV string) (App, error) + GetAppContent(name string, p string, cN string, cV string) (AppContent, error) + DeleteApp(name string, p string, cN string, cV string) error +} + +// AppClient implements the AppManager +// It will also be used to maintain some localized state +type AppClient struct { + storeName string + tagMeta, tagContent string +} + +// NewAppClient returns an instance of the AppClient +// which implements the AppManager +func NewAppClient() *AppClient { + return &AppClient{ + storeName: "orchestrator", + tagMeta: "appmetadata", + tagContent: "appcontent", + } +} + +// CreateApp creates a new collection based on the App +func (v *AppClient) CreateApp(a App, ac AppContent, p string, cN string, cV string) (App, error) { + + //Construct the composite key to select the entry + key := AppKey{ + App: a.Metadata.Name, + Project: p, + CompositeApp: cN, + CompositeAppVersion: cV, + } + + //Check if this App already exists + _, err := v.GetApp(a.Metadata.Name, p, cN, cV) + if err == nil { + return App{}, pkgerrors.New("App already exists") + } + + //Check if Project exists + _, err = NewProjectClient().GetProject(p) + if err != nil { + return App{}, pkgerrors.New("Unable to find the project") + } + + //check if CompositeApp with version exists + _, err = NewCompositeAppClient().GetCompositeApp(cN, cV, p) + if err != nil { + return App{}, pkgerrors.New("Unable to find the composite app with version") + } + + err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, a) + if err != nil { + return App{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + err = db.DBconn.Insert(v.storeName, key, nil, v.tagContent, ac) + if err != nil { + return App{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return a, nil +} + +// GetApp returns the App for corresponding name +func (v *AppClient) GetApp(name string, p string, cN string, cV string) (App, error) { + + //Construct the composite key to select the entry + key := AppKey{ + App: name, + Project: p, + CompositeApp: cN, + CompositeAppVersion: cV, + } + value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return App{}, pkgerrors.Wrap(err, "Get app") + } + + //value is a byte array + if value != nil { + app := App{} + err = db.DBconn.Unmarshal(value[0], &app) + if err != nil { + return App{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return app, nil + } + + return App{}, pkgerrors.New("Error getting app") +} + +// GetAppContent returns content for corresponding app +func (v *AppClient) GetAppContent(name string, p string, cN string, cV string) (AppContent, error) { + + //Construct the composite key to select the entry + key := AppKey{ + App: name, + Project: p, + CompositeApp: cN, + CompositeAppVersion: cV, + } + value, err := db.DBconn.Find(v.storeName, key, v.tagContent) + if err != nil { + return AppContent{}, pkgerrors.Wrap(err, "Get app content") + } + + //value is a byte array + if value != nil { + ac := AppContent{} + err = db.DBconn.Unmarshal(value[0], &ac) + if err != nil { + return AppContent{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return ac, nil + } + + return AppContent{}, pkgerrors.New("Error getting app content") +} + +// DeleteApp deletes the App from database +func (v *AppClient) DeleteApp(name string, p string, cN string, cV string) error { + + //Construct the composite key to select the entry + key := AppKey{ + App: name, + Project: p, + CompositeApp: cN, + CompositeAppVersion: cV, + } + err := db.DBconn.Remove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete App Entry;") + } + + return nil +} diff --git a/src/orchestrator/pkg/module/app_test.go b/src/orchestrator/pkg/module/app_test.go new file mode 100644 index 00000000..42c08ef6 --- /dev/null +++ b/src/orchestrator/pkg/module/app_test.go @@ -0,0 +1,327 @@ +/* + * 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 module + +import ( + "reflect" + "strings" + "testing" + + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + pkgerrors "github.com/pkg/errors" + // pkgerrors "github.com/pkg/errors" +) + +func TestCreateApp(t *testing.T) { + testCases := []struct { + label string + inpApp App + inpAppContent AppContent + inpProject string + inpCompositeAppName string + inpCompositeAppVersion string + expectedError string + mockdb *db.MockDB + expected App + }{ + { + label: "Create App", + inpApp: App{ + Metadata: AppMetaData{ + Name: "testApp", + Description: "A sample app used for unit testing", + UserData1: "userData1", + UserData2: "userData2", + }, + }, + + inpAppContent: AppContent{ + FileContent: "Sample file content", + }, + inpProject: "testProject", + inpCompositeAppName: "testCompositeApp", + inpCompositeAppVersion: "v1", + expected: App{ + Metadata: AppMetaData{ + Name: "testApp", + Description: "A sample app used for unit testing", + UserData1: "userData1", + UserData2: "userData2", + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + ProjectKey{ProjectName: "testProject"}.String(): { + "projectmetadata": []byte( + "{" + + "\"metadata\": {" + + "\"Name\": \"testProject\"," + + "\"Description\": \"Test project for unit testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}" + + "}"), + }, + CompositeAppKey{CompositeAppName: "testCompositeApp", Version: "v1", Project: "testProject"}.String(): { + "compositeapp": []byte( + "{" + + "\"metadata\":{" + + "\"Name\":\"testCompositeApp\"," + + "\"Description\":\"Test CompositeApp for unit testing\"," + + "\"UserData1\":\"userData1\"," + + "\"UserData2\":\"userData2\"}," + + "\"spec\":{" + + "\"Version\":\"v1\"}" + + "}"), + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + impl := NewAppClient() + got, err := impl.CreateApp(testCase.inpApp, testCase.inpAppContent, testCase.inpProject, testCase.inpCompositeAppName, testCase.inpCompositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Create returned an unexpected error %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("Create returned an unexpected error %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("Create returned unexpected body: got %v;"+ + " expected %v", got, testCase.expected) + } + } + }) + } +} + +func TestGetApp(t *testing.T) { + + testCases := []struct { + label string + inpApp string + inpProject string + inpCompositeAppName string + inpCompositeAppVersion string + expectedError string + mockdb *db.MockDB + expected App + }{ + { + label: "Get Composite App", + inpApp: "testApp", + inpProject: "testProject", + inpCompositeAppName: "testCompositeApp", + inpCompositeAppVersion: "v1", + expected: App{ + Metadata: AppMetaData{ + Name: "testApp", + Description: "Test App for unit testing", + UserData1: "userData1", + UserData2: "userData2", + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + AppKey{App: "testApp", Project: "testProject", CompositeApp: "testCompositeApp", CompositeAppVersion: "v1"}.String(): { + "appmetadata": []byte( + "{" + + "\"metadata\": {" + + "\"Name\": \"testApp\"," + + "\"Description\": \"Test App for unit testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}" + + "}"), + "appcontent": []byte( + "{" + + "\"FileContent\": \"sample file content\"" + + "}"), + }, + }, + }, + }, + { + label: "Get Error", + expectedError: "DB Error", + mockdb: &db.MockDB{ + Err: pkgerrors.New("DB Error"), + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + impl := NewAppClient() + got, err := impl.GetApp(testCase.inpApp, testCase.inpProject, testCase.inpCompositeAppName, testCase.inpCompositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Get returned an unexpected error: %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("Get returned an unexpected error: %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("Get returned unexpected body: got %v;"+ + " expected %v", got, testCase.expected) + } + } + }) + } +} + +func TestGetAppContent(t *testing.T) { + + testCases := []struct { + label string + inpApp string + inpProject string + inpCompositeAppName string + inpCompositeAppVersion string + expectedError string + mockdb *db.MockDB + expected AppContent + }{ + { + label: "Get App content", + inpApp: "testApp", + inpProject: "testProject", + inpCompositeAppName: "testCompositeApp", + inpCompositeAppVersion: "v1", + expected: AppContent{ + FileContent: "Samplefilecontent", + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + AppKey{App: "testApp", Project: "testProject", CompositeApp: "testCompositeApp", CompositeAppVersion: "v1"}.String(): { + "appmetadata": []byte( + "{" + + "\"metadata\": {" + + "\"Name\": \"testApp\"," + + "\"Description\": \"Test App for unit testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}" + + "}"), + "appcontent": []byte( + "{" + + "\"FileContent\": \"Samplefilecontent\"" + + "}"), + }, + }, + }, + }, + { + label: "Get Error", + expectedError: "DB Error", + mockdb: &db.MockDB{ + Err: pkgerrors.New("DB Error"), + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + impl := NewAppClient() + got, err := impl.GetAppContent(testCase.inpApp, testCase.inpProject, testCase.inpCompositeAppName, testCase.inpCompositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Get returned an unexpected error: %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("Get returned an unexpected error: %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("Get returned unexpected body: got %v;"+ + " expected %v", got, testCase.expected) + } + } + }) + } +} + +func TestDeleteApp(t *testing.T) { + + testCases := []struct { + label string + inpApp string + inpProject string + inpCompositeAppName string + inpCompositeAppVersion string + expectedError string + mockdb *db.MockDB + }{ + { + label: "Delete App", + inpApp: "testApp", + inpProject: "testProject", + inpCompositeAppName: "testCompositeApp", + inpCompositeAppVersion: "v1", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + AppKey{App: "testApp", Project: "testProject", CompositeApp: "testCompositeApp", CompositeAppVersion: "v1"}.String(): { + "appmetadata": []byte( + "{" + + "\"metadata\": {" + + "\"Name\": \"testApp\"," + + "\"Description\": \"Test App for unit testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}" + + "}"), + "appcontent": []byte( + "{" + + "\"FileContent\": \"Samplefilecontent\"" + + "}"), + }, + }, + }, + }, + { + label: "Delete Error", + expectedError: "DB Error", + mockdb: &db.MockDB{ + Err: pkgerrors.New("DB Error"), + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + impl := NewAppClient() + err := impl.DeleteApp(testCase.inpApp, testCase.inpProject, testCase.inpCompositeAppName, testCase.inpCompositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Delete returned an unexpected error %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("Delete returned an unexpected error %s", err) + } + } + }) + } +} diff --git a/src/orchestrator/pkg/module/compositeapp.go b/src/orchestrator/pkg/module/compositeapp.go index 74fbe0d5..59fbbab5 100644 --- a/src/orchestrator/pkg/module/compositeapp.go +++ b/src/orchestrator/pkg/module/compositeapp.go @@ -105,7 +105,7 @@ func (v *CompositeAppClient) CreateCompositeApp(c CompositeApp, p string) (Compo return CompositeApp{}, pkgerrors.New("Unable to find the project") } - err = db.DBconn.Create(v.storeName, key, v.tagMeta, c) + err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, c) if err != nil { return CompositeApp{}, pkgerrors.Wrap(err, "Creating DB Entry") } @@ -122,7 +122,7 @@ func (v *CompositeAppClient) GetCompositeApp(name string, version string, p stri Version: version, Project: p, } - value, err := db.DBconn.Read(v.storeName, key, v.tagMeta) + value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) if err != nil { return CompositeApp{}, pkgerrors.Wrap(err, "Get composite application") } @@ -130,7 +130,7 @@ func (v *CompositeAppClient) GetCompositeApp(name string, version string, p stri //value is a byte array if value != nil { compApp := CompositeApp{} - err = db.DBconn.Unmarshal(value, &compApp) + err = db.DBconn.Unmarshal(value[0], &compApp) if err != nil { return CompositeApp{}, pkgerrors.Wrap(err, "Unmarshaling Value") } @@ -149,7 +149,7 @@ func (v *CompositeAppClient) DeleteCompositeApp(name string, version string, p s Version: version, Project: p, } - err := db.DBconn.Delete(v.storeName, key, v.tagMeta) + err := db.DBconn.Remove(v.storeName, key) if err != nil { return pkgerrors.Wrap(err, "Delete CompositeApp Entry;") } diff --git a/src/orchestrator/pkg/module/module.go b/src/orchestrator/pkg/module/module.go index 8f2948dd..77a2f5bf 100644 --- a/src/orchestrator/pkg/module/module.go +++ b/src/orchestrator/pkg/module/module.go @@ -20,6 +20,7 @@ package module type Client struct { Project *ProjectClient CompositeApp *CompositeAppClient + App *AppClient Controller *ControllerClient Cluster *ClusterClient GenericPlacementIntent *GenericPlacementIntentClient @@ -36,6 +37,7 @@ func NewClient() *Client { c := &Client{} c.Project = NewProjectClient() c.CompositeApp = NewCompositeAppClient() + c.App = NewAppClient() c.Controller = NewControllerClient() c.Cluster = NewClusterClient() c.GenericPlacementIntent = NewGenericPlacementIntentClient() |