diff options
Diffstat (limited to 'src/orchestrator/pkg/module')
17 files changed, 3155 insertions, 33 deletions
diff --git a/src/orchestrator/pkg/module/add_intents.go b/src/orchestrator/pkg/module/add_intents.go new file mode 100644 index 00000000..a657cce7 --- /dev/null +++ b/src/orchestrator/pkg/module/add_intents.go @@ -0,0 +1,184 @@ +/* + * 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 Addlicable 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 ( + "encoding/json" + "reflect" + + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// Intent shall have 2 fields - MetaData and Spec +type Intent struct { + MetaData IntentMetaData `json:"metadata"` + Spec IntentSpecData `json:"spec"` +} + +// IntentMetaData has Name, Description, userdata1, userdata2 +type IntentMetaData struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// IntentSpecData has Intent +type IntentSpecData struct { + Intent IntentObj `json:"intent"` +} + +// IntentObj has name of the generic placement intent +type IntentObj struct { + Generic string `json:"generic"` +} + +// ListOfIntents is a list of intents +type ListOfIntents struct { + ListOfIntents []map[string]string `json:"intent"` +} + +// IntentManager is an interface which exposes the IntentManager functionality +type IntentManager interface { + AddIntent(a Intent, p string, ca string, v string, di string) (Intent, error) + GetIntent(i string, p string, ca string, v string, di string) (Intent, error) + DeleteIntent(i string, p string, ca string, v string, di string) error +} + +// IntentKey consists of Name if the intent, Project name, CompositeApp name, +// CompositeApp version +type IntentKey struct { + Name string `json:"name"` + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + Version string `json:"version"` + DeploymentIntentGroup string `json:"deployment-intent-group-name"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (ik IntentKey) String() string { + out, err := json.Marshal(ik) + if err != nil { + return "" + } + return string(out) +} + +// IntentClient implements the AddIntentManager interface +type IntentClient struct { + storeName string + tagMetaData string +} + +// NewIntentClient returns an instance of AddIntentClient +func NewIntentClient() *IntentClient { + return &IntentClient{ + storeName: "orchestrator", + tagMetaData: "addintent", + } +} + +// AddIntent adds a given intent to the deployment-intent-group and stores in the db. Other input parameters for it - projectName, compositeAppName, version, DeploymentIntentgroupName +func (c *IntentClient) AddIntent(a Intent, p string, ca string, v string, di string) (Intent, error) { + + //Check for the AddIntent already exists here. + res, err := c.GetIntent(a.MetaData.Name, p, ca, v, di) + if !reflect.DeepEqual(res, Intent{}) { + return Intent{}, pkgerrors.New("AppIntent already exists") + } + + //Check if project exists + _, err = NewProjectClient().GetProject(p) + if err != nil { + return Intent{}, pkgerrors.New("Unable to find the project") + } + + //check if compositeApp exists + _, err = NewCompositeAppClient().GetCompositeApp(ca, v, p) + if err != nil { + return Intent{}, pkgerrors.New("Unable to find the composite-app") + } + + //check if DeploymentIntentGroup exists + _, err = NewDeploymentIntentGroupClient().GetDeploymentIntentGroup(di, p, ca, v) + if err != nil { + return Intent{}, pkgerrors.New("Unable to find the intent") + } + + akey := IntentKey{ + Name: a.MetaData.Name, + Project: p, + CompositeApp: ca, + Version: v, + DeploymentIntentGroup: di, + } + + err = db.DBconn.Insert(c.storeName, akey, nil, c.tagMetaData, a) + if err != nil { + return Intent{}, pkgerrors.Wrap(err, "Create DB entry error") + } + return a, nil +} + +// GetIntent returns an Intent +func (c *IntentClient) GetIntent(i string, p string, ca string, v string, di string) (Intent, error) { + + k := IntentKey{ + Name: i, + Project: p, + CompositeApp: ca, + Version: v, + DeploymentIntentGroup: di, + } + + result, err := db.DBconn.Find(c.storeName, k, c.tagMetaData) + if err != nil { + return Intent{}, pkgerrors.Wrap(err, "Get AppIntent error") + } + + if result != nil { + a := Intent{} + err = db.DBconn.Unmarshal(result[0], &a) + if err != nil { + return Intent{}, pkgerrors.Wrap(err, "Unmarshalling AppIntent") + } + return a, nil + + } + return Intent{}, pkgerrors.New("Error getting AppIntent") +} + +// DeleteIntent deletes a given intent tied to project, composite app and deployment intent group +func (c IntentClient) DeleteIntent(i string, p string, ca string, v string, di string) error { + k := IntentKey{ + Name: i, + Project: p, + CompositeApp: ca, + Version: v, + DeploymentIntentGroup: di, + } + + err := db.DBconn.Remove(c.storeName, k) + if err != nil { + return pkgerrors.Wrap(err, "Delete Project entry;") + } + return nil + +} diff --git a/src/orchestrator/pkg/module/app_intent.go b/src/orchestrator/pkg/module/app_intent.go new file mode 100644 index 00000000..70c80dac --- /dev/null +++ b/src/orchestrator/pkg/module/app_intent.go @@ -0,0 +1,195 @@ +/* + * 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 ( + "encoding/json" + "reflect" + + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// AppIntent has two components - metadata, spec +type AppIntent struct { + MetaData MetaData `json:"metadata"` + Spec SpecData `json:"spec"` +} + +// MetaData has - name, description, userdata1, userdata2 +type MetaData struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// AllOf consists of AnyOfArray and ClusterNames array +type AllOf struct { + ClusterName string `json:"cluster-name,omitempty"` + ClusterLabelName string `json:"cluster-label-name,omitempty"` + AnyOfArray []AnyOf `json:"anyOf,omitempty"` +} + +// AnyOf consists of Array of ClusterLabelNames +type AnyOf struct { + ClusterName string `json:"cluster-name,omitempty"` + ClusterLabelName string `json:"cluster-label-name,omitempty"` +} + +// IntentStruc consists of AllOfArray and AnyOfArray +type IntentStruc struct { + AllOfArray []AllOf `json:"allOf,omitempty"` + AnyOfArray []AnyOf `json:"anyOf,omitempty"` +} + +// SpecData consists of appName and intent +type SpecData struct { + AppName string `json:"app-name"` + Intent IntentStruc `json:"intent"` +} + +// AppIntentManager is an interface which exposes the +// AppIntentManager functionalities +type AppIntentManager interface { + CreateAppIntent(a AppIntent, p string, ca string, v string, i string) (AppIntent, error) + GetAppIntent(ai string, p string, ca string, v string, i string) (AppIntent, error) + DeleteAppIntent(ai string, p string, ca string, v string, i string) error +} + +// AppIntentKey is used as primary key +type AppIntentKey struct { + Name string `json:"name"` + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + Version string `json:"version"` + Intent string `json:"intent-name"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (ak AppIntentKey) String() string { + out, err := json.Marshal(ak) + if err != nil { + return "" + } + return string(out) +} + +// AppIntentClient implements the AppIntentManager interface +type AppIntentClient struct { + storeName string + tagMetaData string +} + +// NewAppIntentClient returns an instance of AppIntentClient +func NewAppIntentClient() *AppIntentClient { + return &AppIntentClient{ + storeName: "orchestrator", + tagMetaData: "appintent", + } +} + +// CreateAppIntent creates an entry for AppIntent in the db. Other input parameters for it - projectName, compositeAppName, version, intentName. +func (c *AppIntentClient) CreateAppIntent(a AppIntent, p string, ca string, v string, i string) (AppIntent, error) { + + //Check for the AppIntent already exists here. + res, err := c.GetAppIntent(a.MetaData.Name, p, ca, v, i) + if !reflect.DeepEqual(res, AppIntent{}) { + return AppIntent{}, pkgerrors.New("AppIntent already exists") + } + + //Check if project exists + _, err = NewProjectClient().GetProject(p) + if err != nil { + return AppIntent{}, pkgerrors.New("Unable to find the project") + } + + // check if compositeApp exists + _, err = NewCompositeAppClient().GetCompositeApp(ca, v, p) + if err != nil { + return AppIntent{}, pkgerrors.New("Unable to find the composite-app") + } + + // check if Intent exists + _, err = NewGenericPlacementIntentClient().GetGenericPlacementIntent(i, p, ca, v) + if err != nil { + return AppIntent{}, pkgerrors.New("Unable to find the intent") + } + + akey := AppIntentKey{ + Name: a.MetaData.Name, + Project: p, + CompositeApp: ca, + Version: v, + Intent: i, + } + + err = db.DBconn.Insert(c.storeName, akey, nil, c.tagMetaData, a) + if err != nil { + return AppIntent{}, pkgerrors.Wrap(err, "Create DB entry error") + } + + return a, nil +} + +// GetAppIntent shall take arguments - name of the app intent, name of the project, name of the composite app, version of the composite app and intent name. It shall return the AppIntent +func (c *AppIntentClient) GetAppIntent(ai string, p string, ca string, v string, i string) (AppIntent, error) { + + k := AppIntentKey{ + Name: ai, + Project: p, + CompositeApp: ca, + Version: v, + Intent: i, + } + + result, err := db.DBconn.Find(c.storeName, k, c.tagMetaData) + if err != nil { + return AppIntent{}, pkgerrors.Wrap(err, "Get AppIntent error") + } + + if result != nil { + a := AppIntent{} + err = db.DBconn.Unmarshal(result[0], &a) + if err != nil { + return AppIntent{}, pkgerrors.Wrap(err, "Unmarshalling AppIntent") + } + return a, nil + + } + return AppIntent{}, pkgerrors.New("Error getting AppIntent") +} + +// DeleteAppIntent delete an AppIntent +func (c *AppIntentClient) DeleteAppIntent(ai string, p string, ca string, v string, i string) error { + k := AppIntentKey{ + Name: ai, + Project: p, + CompositeApp: ca, + Version: v, + Intent: i, + } + + err := db.DBconn.Remove(c.storeName, k) + if err != nil { + return pkgerrors.Wrap(err, "Delete Project entry;") + } + return nil + +} diff --git a/src/orchestrator/pkg/module/app_intent_test.go b/src/orchestrator/pkg/module/app_intent_test.go new file mode 100644 index 00000000..5a4f7693 --- /dev/null +++ b/src/orchestrator/pkg/module/app_intent_test.go @@ -0,0 +1,271 @@ +/* + * 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" +) + +func TestCreateAppIntent(t *testing.T) { + testCases := []struct { + label string + inputAppIntent AppIntent + inputProject string + inputCompositeApp string + inputCompositeAppVersion string + inputGenericPlacementIntent string + expectedError string + mockdb *db.MockDB + expected AppIntent + }{ + { + label: "Create AppIntent", + inputAppIntent: AppIntent{ + MetaData: MetaData{ + Name: "testAppIntent", + Description: "A sample AppIntent", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: SpecData{ + AppName: "SampleApp", + Intent: IntentStruc{ + AllOfArray: []AllOf{ + { + ClusterName: "edge1", + //ClusterLabelName: "edge1", + }, + { + ClusterName: "edge2", + //ClusterLabelName: "edge2", + }, + { + AnyOfArray: []AnyOf{ + {ClusterLabelName: "east-us1"}, + {ClusterLabelName: "east-us2"}, + //{ClusterName: "east-us1"}, + //{ClusterName: "east-us2"}, + }, + }, + }, + + AnyOfArray: []AnyOf{}, + }, + }, + }, + + inputProject: "testProject", + inputCompositeApp: "testCompositeApp", + inputCompositeAppVersion: "testCompositeAppVersion", + inputGenericPlacementIntent: "testIntent", + expected: AppIntent{ + MetaData: MetaData{ + Name: "testAppIntent", + Description: "A sample AppIntent", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: SpecData{ + AppName: "SampleApp", + Intent: IntentStruc{ + AllOfArray: []AllOf{ + { + ClusterName: "edge1", + //ClusterLabelName: "edge1", + }, + { + ClusterName: "edge2", + //ClusterLabelName: "edge2", + }, + { + AnyOfArray: []AnyOf{ + {ClusterLabelName: "east-us1"}, + {ClusterLabelName: "east-us2"}, + //{ClusterName: "east-us1"}, + //{ClusterName: "east-us2"}, + }, + }, + }, + AnyOfArray: []AnyOf{}, + }, + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + ProjectKey{ProjectName: "testProject"}.String(): { + "projectmetadata": []byte( + "{\"project-name\":\"testProject\"," + + "\"description\":\"Test project for unit testing\"}"), + }, + CompositeAppKey{CompositeAppName: "testCompositeApp", + Version: "testCompositeAppVersion", Project: "testProject"}.String(): { + "compositeAppmetadata": []byte( + "{\"metadata\":{" + + "\"name\":\"testCompositeApp\"," + + "\"description\":\"description\"," + + "\"userData1\":\"user data\"," + + "\"userData2\":\"user data\"" + + "}," + + "\"spec\":{" + + "\"version\":\"version of the composite app\"}}"), + }, + GenericPlacementIntentKey{ + Name: "testIntent", + Project: "testProject", + CompositeApp: "testCompositeApp", + Version: "testCompositeAppVersion", + }.String(): { + "genericplacementintent": []byte( + "{\"metadata\":{\"Name\":\"testIntent\"," + + "\"Description\":\"A sample intent for testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}," + + "\"spec\":{\"Logical-Cloud\": \"logicalCloud1\"}}"), + }, + }, + }, + }, + } + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + appIntentCli := NewAppIntentClient() + got, err := appIntentCli.CreateAppIntent(testCase.inputAppIntent, testCase.inputProject, testCase.inputCompositeApp, testCase.inputCompositeAppVersion, testCase.inputGenericPlacementIntent) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("CreateAppIntent returned an unexpected error %s, ", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("CreateAppIntent returned an unexpected error %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("CreateAppIntent returned unexpected body: got %v; "+" expected %v", got, testCase.expected) + } + } + }) + } +} + +func TestGetAppIntent(t *testing.T) { + testCases := []struct { + label string + expectedError string + expected AppIntent + mockdb *db.MockDB + appIntentName string + projectName string + compositeAppName string + compositeAppVersion string + genericPlacementIntent string + }{ + { + label: "Get Intent", + appIntentName: "testAppIntent", + projectName: "testProject", + compositeAppName: "testCompositeApp", + compositeAppVersion: "testCompositeAppVersion", + genericPlacementIntent: "testIntent", + expected: AppIntent{ + MetaData: MetaData{ + Name: "testAppIntent", + Description: "testAppIntent", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: SpecData{ + AppName: "SampleApp", + Intent: IntentStruc{ + AllOfArray: []AllOf{ + { + ClusterName: "edge1", + }, + { + ClusterName: "edge2", + }, + { + AnyOfArray: []AnyOf{ + {ClusterLabelName: "east-us1"}, + {ClusterLabelName: "east-us2"}, + }, + }, + }, + }, + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + AppIntentKey{ + Name: "testAppIntent", + Project: "testProject", + CompositeApp: "testCompositeApp", + Version: "testCompositeAppVersion", + Intent: "testIntent", + }.String(): { + "appintent": []byte( + "{\"metadata\":{\"Name\":\"testAppIntent\"," + + "\"Description\":\"testAppIntent\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}," + + "\"spec\":{\"app-name\": \"SampleApp\"," + + "\"intent\": {" + + "\"allOf\":[" + + "{\"cluster-name\":\"edge1\"}," + + "{\"cluster-name\":\"edge2\"}," + + "{" + + "\"anyOf\":[" + + "{" + + "\"cluster-label-name\":\"east-us1\"}," + + "{" + + "\"cluster-label-name\":\"east-us2\"}" + + "]}]" + + "}}}"), + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + appIntentCli := NewAppIntentClient() + got, err := appIntentCli.GetAppIntent(testCase.appIntentName, testCase.projectName, testCase.compositeAppName, testCase.compositeAppVersion, + testCase.genericPlacementIntent) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("GetAppIntent returned an unexpected error: %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("GetAppIntent returned an unexpected error: %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("GetAppIntent returned unexpected body: got %v;"+ + " expected %v", got, testCase.expected) + } + } + + }) + } +} diff --git a/src/orchestrator/pkg/module/app_profile.go b/src/orchestrator/pkg/module/app_profile.go new file mode 100644 index 00000000..77835fb4 --- /dev/null +++ b/src/orchestrator/pkg/module/app_profile.go @@ -0,0 +1,296 @@ +/* + * 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 ( + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// AppProfile contains the parameters needed for AppProfiles +// It implements the interface for managing the AppProfiles +type AppProfile struct { + Metadata AppProfileMetadata `json:"metadata"` + Spec AppProfileSpec `json:"spec"` +} + +type AppProfileContent struct { + Profile string `json:"profile"` +} + +// AppProfileMetadata contains the metadata for AppProfiles +type AppProfileMetadata struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// AppProfileSpec contains the Spec for AppProfiles +type AppProfileSpec struct { + AppName string `json:"app-name"` +} + +// AppProfileKey is the key structure that is used in the database +type AppProfileKey struct { + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + CompositeAppVersion string `json:"compositeappversion"` + CompositeProfile string `json:"compositeprofile"` + Profile string `json:"profile"` +} + +type AppProfileQueryKey struct { + AppName string `json:"app-name"` +} + +type AppProfileFindByAppKey struct { + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + CompositeAppVersion string `json:"compositeappversion"` + CompositeProfile string `json:"compositeprofile"` + AppName string `json:"app-name"` +} + +// AppProfileManager exposes the AppProfile functionality +type AppProfileManager interface { + CreateAppProfile(provider, compositeApp, compositeAppVersion, compositeProfile string, ap AppProfile, ac AppProfileContent) (AppProfile, error) + GetAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, profile string) (AppProfile, error) + GetAppProfiles(project, compositeApp, compositeAppVersion, compositeProfile string) ([]AppProfile, error) + GetAppProfileByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName string) (AppProfile, error) + GetAppProfileContent(project, compositeApp, compositeAppVersion, compositeProfile, profile string) (AppProfileContent, error) + GetAppProfileContentByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName string) (AppProfileContent, error) + DeleteAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, profile string) error +} + +// AppProfileClient implements the Manager +// It will also be used to maintain some localized state +type AppProfileClient struct { + storeName string + tagMeta string + tagContent string +} + +// NewAppProfileClient returns an instance of the AppProfileClient +// which implements the Manager +func NewAppProfileClient() *AppProfileClient { + return &AppProfileClient{ + storeName: "orchestrator", + tagMeta: "profilemetadata", + tagContent: "profilecontent", + } +} + +// CreateAppProfile creates an entry for AppProfile in the database. +func (c *AppProfileClient) CreateAppProfile(project, compositeApp, compositeAppVersion, compositeProfile string, ap AppProfile, ac AppProfileContent) (AppProfile, error) { + key := AppProfileKey{ + Project: project, + CompositeApp: compositeApp, + CompositeAppVersion: compositeAppVersion, + CompositeProfile: compositeProfile, + Profile: ap.Metadata.Name, + } + qkey := AppProfileQueryKey{ + AppName: ap.Spec.AppName, + } + + res, err := c.GetAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, ap.Metadata.Name) + if res != (AppProfile{}) { + return AppProfile{}, pkgerrors.New("AppProfile already exists") + } + + res, err = c.GetAppProfileByApp(project, compositeApp, compositeAppVersion, compositeProfile, ap.Spec.AppName) + if res != (AppProfile{}) { + return AppProfile{}, pkgerrors.New("App already has an AppProfile") + } + + //Check if composite profile exists (success assumes existance of all higher level 'parent' objects) + _, err = NewCompositeProfileClient().GetCompositeProfile(compositeProfile, project, compositeApp, compositeAppVersion) + if err != nil { + return AppProfile{}, pkgerrors.New("Unable to find the project") + } + + // TODO: (after app api is ready) check that the app Spec.AppName exists as part of the composite app + + err = db.DBconn.Insert(c.storeName, key, qkey, c.tagMeta, ap) + if err != nil { + return AppProfile{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + err = db.DBconn.Insert(c.storeName, key, qkey, c.tagContent, ac) + if err != nil { + return AppProfile{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return ap, nil +} + +// GetAppProfile - return specified App Profile +func (c *AppProfileClient) GetAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, profile string) (AppProfile, error) { + key := AppProfileKey{ + Project: project, + CompositeApp: compositeApp, + CompositeAppVersion: compositeAppVersion, + CompositeProfile: compositeProfile, + Profile: profile, + } + + value, err := db.DBconn.Find(c.storeName, key, c.tagMeta) + if err != nil { + return AppProfile{}, pkgerrors.Wrap(err, "Get App Profile error") + } + + if value != nil { + ap := AppProfile{} + err = db.DBconn.Unmarshal(value[0], &ap) + if err != nil { + return AppProfile{}, pkgerrors.Wrap(err, "Unmarshalling AppProfile") + } + return ap, nil + } + + return AppProfile{}, pkgerrors.New("Error getting AppProfile") + +} + +// GetAppProfile - return all App Profiles for given composite profile +func (c *AppProfileClient) GetAppProfiles(project, compositeApp, compositeAppVersion, compositeProfile string) ([]AppProfile, error) { + + key := AppProfileKey{ + Project: project, + CompositeApp: compositeApp, + CompositeAppVersion: compositeAppVersion, + CompositeProfile: compositeProfile, + Profile: "", + } + + var resp []AppProfile + values, err := db.DBconn.Find(c.storeName, key, c.tagMeta) + if err != nil { + return []AppProfile{}, pkgerrors.Wrap(err, "Get AppProfiles") + } + + for _, value := range values { + ap := AppProfile{} + err = db.DBconn.Unmarshal(value, &ap) + if err != nil { + return []AppProfile{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + resp = append(resp, ap) + } + + return resp, nil +} + +// GetAppProfileByApp - return all App Profiles for given composite profile +func (c *AppProfileClient) GetAppProfileByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName string) (AppProfile, error) { + + key := AppProfileFindByAppKey{ + Project: project, + CompositeApp: compositeApp, + CompositeAppVersion: compositeAppVersion, + CompositeProfile: compositeProfile, + AppName: appName, + } + + value, err := db.DBconn.Find(c.storeName, key, c.tagMeta) + if err != nil { + return AppProfile{}, pkgerrors.Wrap(err, "Get AppProfile by App") + } + + if value != nil { + ap := AppProfile{} + err = db.DBconn.Unmarshal(value[0], &ap) + if err != nil { + return AppProfile{}, pkgerrors.Wrap(err, "Unmarshalling AppProfile") + } + return ap, nil + } + + return AppProfile{}, pkgerrors.New("Error getting AppProfile by App") +} + +func (c *AppProfileClient) GetAppProfileContent(project, compositeApp, compositeAppVersion, compositeProfile, profile string) (AppProfileContent, error) { + key := AppProfileKey{ + Project: project, + CompositeApp: compositeApp, + CompositeAppVersion: compositeAppVersion, + CompositeProfile: compositeProfile, + Profile: profile, + } + + value, err := db.DBconn.Find(c.storeName, key, c.tagContent) + if err != nil { + return AppProfileContent{}, pkgerrors.Wrap(err, "Get Cluster Content") + } + + //value is a byte array + if value != nil { + ac := AppProfileContent{} + err = db.DBconn.Unmarshal(value[0], &ac) + if err != nil { + return AppProfileContent{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return ac, nil + } + + return AppProfileContent{}, pkgerrors.New("Error getting App Profile Content") +} + +func (c *AppProfileClient) GetAppProfileContentByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName string) (AppProfileContent, error) { + key := AppProfileFindByAppKey{ + Project: project, + CompositeApp: compositeApp, + CompositeAppVersion: compositeAppVersion, + CompositeProfile: compositeProfile, + AppName: appName, + } + + value, err := db.DBconn.Find(c.storeName, key, c.tagContent) + if err != nil { + return AppProfileContent{}, pkgerrors.Wrap(err, "Get Cluster Content") + } + + //value is a byte array + if value != nil { + ac := AppProfileContent{} + err = db.DBconn.Unmarshal(value[0], &ac) + if err != nil { + return AppProfileContent{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return ac, nil + } + + return AppProfileContent{}, pkgerrors.New("Error getting App Profile Content") +} + +// Delete AppProfile from the database +func (c *AppProfileClient) DeleteAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, profile string) error { + key := AppProfileKey{ + Project: project, + CompositeApp: compositeApp, + CompositeAppVersion: compositeAppVersion, + CompositeProfile: compositeProfile, + Profile: profile, + } + + err := db.DBconn.Remove(c.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete AppProfile entry;") + } + return nil +} diff --git a/src/orchestrator/pkg/module/cluster.go b/src/orchestrator/pkg/module/cluster.go new file mode 100644 index 00000000..c9ddad6e --- /dev/null +++ b/src/orchestrator/pkg/module/cluster.go @@ -0,0 +1,540 @@ +/* + * 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 ( + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// ClusterProvider contains the parameters needed for ClusterProviders +// It implements the interface for managing the ClusterProviders +type Metadata struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +type ClusterProvider struct { + Metadata Metadata `json:"metadata"` +} + +type Cluster struct { + Metadata Metadata `json:"metadata"` +} + +type ClusterContent struct { + Kubeconfig string `json:"kubeconfig"` +} + +type ClusterLabel struct { + LabelName string `json:"label-name"` +} + +type ClusterKvPairs struct { + Metadata Metadata `json:"metadata"` + Spec ClusterKvSpec `json:"spec"` +} + +type ClusterKvSpec struct { + Kv []map[string]interface{} `json:"kv"` +} + +// ClusterProviderKey is the key structure that is used in the database +type ClusterProviderKey struct { + ClusterProviderName string `json:"provider"` +} + +// ClusterKey is the key structure that is used in the database +type ClusterKey struct { + ClusterProviderName string `json:"provider"` + ClusterName string `json:"cluster"` +} + +// ClusterLabelKey is the key structure that is used in the database +type ClusterLabelKey struct { + ClusterProviderName string `json:"provider"` + ClusterName string `json:"cluster"` + ClusterLabelName string `json:"label"` +} + +// ClusterKvPairsKey is the key structure that is used in the database +type ClusterKvPairsKey struct { + ClusterProviderName string `json:"provider"` + ClusterName string `json:"cluster"` + ClusterKvPairsName string `json:"kvname"` +} + +// Manager is an interface exposes the Cluster functionality +type ClusterManager interface { + CreateClusterProvider(pr ClusterProvider) (ClusterProvider, error) + GetClusterProvider(name string) (ClusterProvider, error) + GetClusterProviders() ([]ClusterProvider, error) + DeleteClusterProvider(name string) error + CreateCluster(provider string, pr Cluster, qr ClusterContent) (Cluster, error) + GetCluster(provider, name string) (Cluster, error) + GetClusterContent(provider, name string) (ClusterContent, error) + GetClusters(provider string) ([]Cluster, error) + DeleteCluster(provider, name string) error + CreateClusterLabel(provider, cluster string, pr ClusterLabel) (ClusterLabel, error) + GetClusterLabel(provider, cluster, label string) (ClusterLabel, error) + GetClusterLabels(provider, cluster string) ([]ClusterLabel, error) + DeleteClusterLabel(provider, cluster, label string) error + CreateClusterKvPairs(provider, cluster string, pr ClusterKvPairs) (ClusterKvPairs, error) + GetClusterKvPairs(provider, cluster, kvpair string) (ClusterKvPairs, error) + GetAllClusterKvPairs(provider, cluster string) ([]ClusterKvPairs, error) + DeleteClusterKvPairs(provider, cluster, kvpair string) error +} + +// ClusterClient implements the Manager +// It will also be used to maintain some localized state +type ClusterClient struct { + storeName string + tagMeta string + tagContent string +} + +// NewClusterClient returns an instance of the ClusterClient +// which implements the Manager +func NewClusterClient() *ClusterClient { + return &ClusterClient{ + storeName: "cluster", + tagMeta: "clustermetadata", + tagContent: "clustercontent", + } +} + +// CreateClusterProvider - create a new Cluster Provider +func (v *ClusterClient) CreateClusterProvider(p ClusterProvider) (ClusterProvider, error) { + + //Construct key and tag to select the entry + key := ClusterProviderKey{ + ClusterProviderName: p.Metadata.Name, + } + + //Check if this ClusterProvider already exists + _, err := v.GetClusterProvider(p.Metadata.Name) + if err == nil { + return ClusterProvider{}, pkgerrors.New("ClusterProvider already exists") + } + + err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + if err != nil { + return ClusterProvider{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return p, nil +} + +// GetClusterProvider returns the ClusterProvider for corresponding name +func (v *ClusterClient) GetClusterProvider(name string) (ClusterProvider, error) { + + //Construct key and tag to select the entry + key := ClusterProviderKey{ + ClusterProviderName: name, + } + + value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return ClusterProvider{}, pkgerrors.Wrap(err, "Get ClusterProvider") + } + + //value is a byte array + if value != nil { + cp := ClusterProvider{} + err = db.DBconn.Unmarshal(value[0], &cp) + if err != nil { + return ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return cp, nil + } + + return ClusterProvider{}, pkgerrors.New("Error getting ClusterProvider") +} + +// GetClusterProviderList returns all of the ClusterProvider for corresponding name +func (v *ClusterClient) GetClusterProviders() ([]ClusterProvider, error) { + + //Construct key and tag to select the entry + key := ClusterProviderKey{ + ClusterProviderName: "", + } + + var resp []ClusterProvider + values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return []ClusterProvider{}, pkgerrors.Wrap(err, "Get ClusterProviders") + } + + for _, value := range values { + cp := ClusterProvider{} + err = db.DBconn.Unmarshal(value, &cp) + if err != nil { + return []ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + resp = append(resp, cp) + } + + return resp, nil +} + +// DeleteClusterProvider the ClusterProvider from database +func (v *ClusterClient) DeleteClusterProvider(name string) error { + + //Construct key and tag to select the entry + key := ClusterProviderKey{ + ClusterProviderName: name, + } + + err := db.DBconn.Remove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete ClusterProvider Entry;") + } + + return nil +} + +// CreateCluster - create a new Cluster for a cluster-provider +func (v *ClusterClient) CreateCluster(provider string, p Cluster, q ClusterContent) (Cluster, error) { + + //Construct key and tag to select the entry + key := ClusterKey{ + ClusterProviderName: provider, + ClusterName: p.Metadata.Name, + } + + //Verify ClusterProvider already exists + _, err := v.GetClusterProvider(provider) + if err != nil { + return Cluster{}, pkgerrors.New("ClusterProvider does not exist") + } + + //Check if this Cluster already exists + _, err = v.GetCluster(provider, p.Metadata.Name) + if err == nil { + return Cluster{}, pkgerrors.New("Cluster already exists") + } + + err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + err = db.DBconn.Insert(v.storeName, key, nil, v.tagContent, q) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return p, nil +} + +// GetCluster returns the Cluster for corresponding provider and name +func (v *ClusterClient) GetCluster(provider, name string) (Cluster, error) { + //Construct key and tag to select the entry + key := ClusterKey{ + ClusterProviderName: provider, + ClusterName: name, + } + + value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Get Cluster") + } + + //value is a byte array + if value != nil { + cl := Cluster{} + err = db.DBconn.Unmarshal(value[0], &cl) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return cl, nil + } + + return Cluster{}, pkgerrors.New("Error getting Cluster") +} + +// GetClusterContent returns the ClusterContent for corresponding provider and name +func (v *ClusterClient) GetClusterContent(provider, name string) (ClusterContent, error) { + //Construct key and tag to select the entry + key := ClusterKey{ + ClusterProviderName: provider, + ClusterName: name, + } + + value, err := db.DBconn.Find(v.storeName, key, v.tagContent) + if err != nil { + return ClusterContent{}, pkgerrors.Wrap(err, "Get Cluster Content") + } + + //value is a byte array + if value != nil { + cc := ClusterContent{} + err = db.DBconn.Unmarshal(value[0], &cc) + if err != nil { + return ClusterContent{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return cc, nil + } + + return ClusterContent{}, pkgerrors.New("Error getting Cluster Content") +} + +// GetClusters returns all the Clusters for corresponding provider +func (v *ClusterClient) GetClusters(provider string) ([]Cluster, error) { + //Construct key and tag to select the entry + key := ClusterKey{ + ClusterProviderName: provider, + ClusterName: "", + } + + values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return []Cluster{}, pkgerrors.Wrap(err, "Get Clusters") + } + + var resp []Cluster + + for _, value := range values { + cp := Cluster{} + err = db.DBconn.Unmarshal(value, &cp) + if err != nil { + return []Cluster{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + resp = append(resp, cp) + } + + return resp, nil +} + +// DeleteCluster the Cluster from database +func (v *ClusterClient) DeleteCluster(provider, name string) error { + //Construct key and tag to select the entry + key := ClusterKey{ + ClusterProviderName: provider, + ClusterName: name, + } + + err := db.DBconn.Remove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Cluster Entry;") + } + + return nil +} + +// CreateClusterLabel - create a new Cluster Label mongo document for a cluster-provider/cluster +func (v *ClusterClient) CreateClusterLabel(provider string, cluster string, p ClusterLabel) (ClusterLabel, error) { + //Construct key and tag to select the entry + key := ClusterLabelKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterLabelName: p.LabelName, + } + + //Verify Cluster already exists + _, err := v.GetCluster(provider, cluster) + if err != nil { + return ClusterLabel{}, pkgerrors.New("Cluster does not exist") + } + + //Check if this ClusterLabel already exists + _, err = v.GetClusterLabel(provider, cluster, p.LabelName) + if err == nil { + return ClusterLabel{}, pkgerrors.New("Cluster Label already exists") + } + + err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + if err != nil { + return ClusterLabel{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return p, nil +} + +// GetClusterLabel returns the Cluster for corresponding provider, cluster and label +func (v *ClusterClient) GetClusterLabel(provider, cluster, label string) (ClusterLabel, error) { + //Construct key and tag to select the entry + key := ClusterLabelKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterLabelName: label, + } + + value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return ClusterLabel{}, pkgerrors.Wrap(err, "Get Cluster") + } + + //value is a byte array + if value != nil { + cl := ClusterLabel{} + err = db.DBconn.Unmarshal(value[0], &cl) + if err != nil { + return ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return cl, nil + } + + return ClusterLabel{}, pkgerrors.New("Error getting Cluster") +} + +// GetClusterLabels returns the Cluster Labels for corresponding provider and cluster +func (v *ClusterClient) GetClusterLabels(provider, cluster string) ([]ClusterLabel, error) { + //Construct key and tag to select the entry + key := ClusterLabelKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterLabelName: "", + } + + values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return []ClusterLabel{}, pkgerrors.Wrap(err, "Get Cluster Labels") + } + + var resp []ClusterLabel + + for _, value := range values { + cp := ClusterLabel{} + err = db.DBconn.Unmarshal(value, &cp) + if err != nil { + return []ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + resp = append(resp, cp) + } + + return resp, nil +} + +// Delete the Cluster Label from database +func (v *ClusterClient) DeleteClusterLabel(provider, cluster, label string) error { + //Construct key and tag to select the entry + key := ClusterLabelKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterLabelName: label, + } + + err := db.DBconn.Remove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete ClusterLabel Entry;") + } + + return nil +} + +// CreateClusterKvPairs - Create a New Cluster KV pairs document +func (v *ClusterClient) CreateClusterKvPairs(provider string, cluster string, p ClusterKvPairs) (ClusterKvPairs, error) { + key := ClusterKvPairsKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterKvPairsName: p.Metadata.Name, + } + + //Verify Cluster already exists + _, err := v.GetCluster(provider, cluster) + if err != nil { + return ClusterKvPairs{}, pkgerrors.New("Cluster does not exist") + } + + //Check if this ClusterKvPairs already exists + _, err = v.GetClusterKvPairs(provider, cluster, p.Metadata.Name) + if err == nil { + return ClusterKvPairs{}, pkgerrors.New("Cluster KV Pair already exists") + } + + err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + if err != nil { + return ClusterKvPairs{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return p, nil +} + +// GetClusterKvPairs returns the Cluster KeyValue pair for corresponding provider, cluster and KV pair name +func (v *ClusterClient) GetClusterKvPairs(provider, cluster, kvpair string) (ClusterKvPairs, error) { + //Construct key and tag to select entry + key := ClusterKvPairsKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterKvPairsName: kvpair, + } + + value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return ClusterKvPairs{}, pkgerrors.Wrap(err, "Get Cluster") + } + + //value is a byte array + if value != nil { + ckvp := ClusterKvPairs{} + err = db.DBconn.Unmarshal(value[0], &ckvp) + if err != nil { + return ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return ckvp, nil + } + + return ClusterKvPairs{}, pkgerrors.New("Error getting Cluster") +} + +// GetAllClusterKvPairs returns the Cluster Kv Pairs for corresponding provider and cluster +func (v *ClusterClient) GetAllClusterKvPairs(provider, cluster string) ([]ClusterKvPairs, error) { + //Construct key and tag to select the entry + key := ClusterKvPairsKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterKvPairsName: "", + } + + values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + if err != nil { + return []ClusterKvPairs{}, pkgerrors.Wrap(err, "Get Cluster KV Pairs") + } + + var resp []ClusterKvPairs + + for _, value := range values { + cp := ClusterKvPairs{} + err = db.DBconn.Unmarshal(value, &cp) + if err != nil { + return []ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + resp = append(resp, cp) + } + + return resp, nil +} + +// DeleteClusterKvPairs the ClusterKvPairs from database +func (v *ClusterClient) DeleteClusterKvPairs(provider, cluster, kvpair string) error { + //Construct key and tag to select entry + key := ClusterKvPairsKey{ + ClusterProviderName: provider, + ClusterName: cluster, + ClusterKvPairsName: kvpair, + } + + err := db.DBconn.Remove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete ClusterKvPairs Entry;") + } + + return nil +} diff --git a/src/orchestrator/pkg/module/composite_profile.go b/src/orchestrator/pkg/module/composite_profile.go new file mode 100644 index 00000000..dca2116a --- /dev/null +++ b/src/orchestrator/pkg/module/composite_profile.go @@ -0,0 +1,192 @@ +/* + * 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 ( + "encoding/json" + + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// CompositeProfile contains the parameters needed for CompositeProfiles +// It implements the interface for managing the CompositeProfiles +type CompositeProfile struct { + Metadata CompositeProfileMetadata `json:"metadata"` +} + +// CompositeProfileMetadata contains the metadata for CompositeProfiles +type CompositeProfileMetadata struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// CompositeProfileKey is the key structure that is used in the database +type CompositeProfileKey struct { + Name string `json:"compositeprofile"` + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + Version string `json:"compositeappversion"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (cpk CompositeProfileKey) String() string { + out, err := json.Marshal(cpk) + if err != nil { + return "" + } + + return string(out) +} + +// CompositeProfileManager exposes the CompositeProfile functionality +type CompositeProfileManager interface { + CreateCompositeProfile(cpf CompositeProfile, p string, ca string, + v string) (CompositeProfile, error) + GetCompositeProfile(compositeProfileName string, projectName string, + compositeAppName string, version string) (CompositeProfile, error) + GetCompositeProfiles(projectName string, compositeAppName string, + version string) ([]CompositeProfile, error) + DeleteCompositeProfile(compositeProfileName string, projectName string, + compositeAppName string, version string) error +} + +// CompositeProfileClient implements the Manager +// It will also be used to maintain some localized state +type CompositeProfileClient struct { + storeName string + tagMeta string +} + +// NewCompositeProfileClient returns an instance of the CompositeProfileClient +// which implements the Manager +func NewCompositeProfileClient() *CompositeProfileClient { + return &CompositeProfileClient{ + storeName: "orchestrator", + tagMeta: "compositeprofilemetadata", + } +} + +// CreateCompositeProfile creates an entry for CompositeProfile in the database. Other Input parameters for it - projectName, compositeAppName, version +func (c *CompositeProfileClient) CreateCompositeProfile(cpf CompositeProfile, p string, ca string, + v string) (CompositeProfile, error) { + + res, err := c.GetCompositeProfile(cpf.Metadata.Name, p, ca, v) + if res != (CompositeProfile{}) { + return CompositeProfile{}, pkgerrors.New("CompositeProfile already exists") + } + + //Check if project exists + _, err = NewProjectClient().GetProject(p) + if err != nil { + return CompositeProfile{}, pkgerrors.New("Unable to find the project") + } + + // check if compositeApp exists + _, err = NewCompositeAppClient().GetCompositeApp(ca, v, p) + if err != nil { + return CompositeProfile{}, pkgerrors.New("Unable to find the composite-app") + } + + cProfkey := CompositeProfileKey{ + Name: cpf.Metadata.Name, + Project: p, + CompositeApp: ca, + Version: v, + } + + err = db.DBconn.Insert(c.storeName, cProfkey, nil, c.tagMeta, cpf) + if err != nil { + return CompositeProfile{}, pkgerrors.Wrap(err, "Create DB entry error") + } + + return cpf, nil +} + +// GetCompositeProfile shall take arguments - name of the composite profile, name of //// the project, name of the composite app and version of the composite app. It shall return the CompositeProfile if its present. +func (c *CompositeProfileClient) GetCompositeProfile(cpf string, p string, ca string, v string) (CompositeProfile, error) { + key := CompositeProfileKey{ + Name: cpf, + Project: p, + CompositeApp: ca, + Version: v, + } + + result, err := db.DBconn.Find(c.storeName, key, c.tagMeta) + if err != nil { + return CompositeProfile{}, pkgerrors.Wrap(err, "Get Composite Profile error") + } + + if result != nil { + cProf := CompositeProfile{} + err = db.DBconn.Unmarshal(result[0], &cProf) + if err != nil { + return CompositeProfile{}, pkgerrors.Wrap(err, "Unmarshalling CompositeProfile") + } + return cProf, nil + } + + return CompositeProfile{}, pkgerrors.New("Error getting CompositeProfile") +} + +// GetCompositeProfile shall take arguments - name of the composite profile, name of //// the project, name of the composite app and version of the composite app. It shall return the CompositeProfile if its present. +func (c *CompositeProfileClient) GetCompositeProfiles(p string, ca string, v string) ([]CompositeProfile, error) { + key := CompositeProfileKey{ + Name: "", + Project: p, + CompositeApp: ca, + Version: v, + } + + values, err := db.DBconn.Find(c.storeName, key, c.tagMeta) + if err != nil { + return []CompositeProfile{}, pkgerrors.Wrap(err, "Get Composite Profiles error") + } + + var resp []CompositeProfile + + for _, value := range values { + cp := CompositeProfile{} + err = db.DBconn.Unmarshal(value, &cp) + if err != nil { + return []CompositeProfile{}, pkgerrors.Wrap(err, "Get Composite Profiles unmarshalling error") + } + resp = append(resp, cp) + } + + return resp, nil +} + +// DeleteCompositeProfile the intent from the database +func (c *CompositeProfileClient) DeleteCompositeProfile(cpf string, p string, ca string, v string) error { + key := CompositeProfileKey{ + Name: cpf, + Project: p, + CompositeApp: ca, + Version: v, + } + + err := db.DBconn.Remove(c.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete CompositeProfile entry;") + } + return nil +} diff --git a/src/orchestrator/pkg/module/composite_profile_test.go b/src/orchestrator/pkg/module/composite_profile_test.go new file mode 100644 index 00000000..af0dd7b7 --- /dev/null +++ b/src/orchestrator/pkg/module/composite_profile_test.go @@ -0,0 +1,175 @@ +/* + * 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 + +//pkgerrors "github.com/pkg/errors" + +/* TODO - db.MockDB needs to be enhanced and then these can be fixed up +func TestCreateCompositeProfile(t *testing.T) { + testCases := []struct { + label string + compositeProfile CompositeProfile + projectName string + compositeApp string + compositeAppVersion string + expectedError string + mockdb *db.MockDB + expected CompositeProfile + }{ + { + label: "Create CompositeProfile", + compositeProfile: CompositeProfile{ + Metadata: CompositeProfileMetadata{ + Name: "testCompositeProfile", + Description: "A sample Composite Profile for testing", + UserData1: "userData1", + UserData2: "userData2", + }, + }, + projectName: "testProject", + compositeApp: "testCompositeApp", + compositeAppVersion: "v1", + expected: CompositeProfile{ + Metadata: CompositeProfileMetadata{ + Name: "testCompositeProfile", + Description: "A sample Composite Profile for 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", Project: "testProject", Version: "v1"}.String(): { + "compositeAppmetadata": []byte( + "{" + + "\"metadata\" : {" + + "\"Name\":\"testCompositeApp\"," + + "\"Description\":\"Test Composite App 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 + cprofCli := NewCompositeProfileClient() + got, err := cprofCli.CreateCompositeProfile(testCase.compositeProfile, testCase.projectName, testCase.compositeApp, testCase.compositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("CreateCompositeProfile returned an unexpected error %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("CreateCompositeProfile returned an unexpected error %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("CreateCompositeProfile returned unexpected body: got %v; "+" expected %v", got, testCase.expected) + } + } + }) + + } +} + +func TestGetCompositeProfile(t *testing.T) { + + testCases := []struct { + label string + expectedError string + expected CompositeProfile + mockdb *db.MockDB + compositeProfileName string + projectName string + compositeAppName string + compositeAppVersion string + }{ + { + label: "Get CompositeProfile", + compositeProfileName: "testCompositeProfile", + projectName: "testProject", + compositeAppName: "testCompositeApp", + compositeAppVersion: "v1", + expected: CompositeProfile{ + Metadata: CompositeProfileMetadata{ + Name: "testCompositeProfile", + Description: "A sample CompositeProfile for testing", + UserData1: "userData1", + UserData2: "userData2", + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + CompositeProfileKey{ + Name: "testCompositeProfile", + Project: "testProject", + CompositeApp: "testCompositeApp", + Version: "v1", + }.String(): { + "compositeprofile": []byte( + "{\"metadata\":{\"Name\":\"testCompositeProfile\"," + + "\"Description\":\"A sample CompositeProfile for testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}}"), + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + cprofCli := NewCompositeProfileClient() + got, err := cprofCli.GetCompositeProfile(testCase.compositeProfileName, testCase.projectName, testCase.compositeAppName, testCase.compositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("GetCompositeProfile returned an unexpected error: %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("GetCompositeProfile returned an unexpected error: %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("GetCompositeProfile returned unexpected body: got %v;"+ + " expected %v", got, testCase.expected) + } + } + + }) + } + +} +*/ 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/controller.go b/src/orchestrator/pkg/module/controller.go new file mode 100644 index 00000000..35d6f892 --- /dev/null +++ b/src/orchestrator/pkg/module/controller.go @@ -0,0 +1,135 @@ +/* + * 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 ( + "encoding/json" + + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// Controller contains the parameters needed for Controllers +// It implements the interface for managing the Controllers +type Controller struct { + Name string `json:"name"` + + Host string `json:"host"` + + Port int64 `json:"port"` +} + +// ControllerKey is the key structure that is used in the database +type ControllerKey struct { + ControllerName string `json:"controller-name"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (mk ControllerKey) String() string { + out, err := json.Marshal(mk) + if err != nil { + return "" + } + + return string(out) +} + +// ControllerManager is an interface exposes the Controller functionality +type ControllerManager interface { + CreateController(ms Controller) (Controller, error) + GetController(name string) (Controller, error) + DeleteController(name string) error +} + +// ControllerClient implements the Manager +// It will also be used to maintain some localized state +type ControllerClient struct { + collectionName string + tagMeta string +} + +// NewControllerClient returns an instance of the ControllerClient +// which implements the Manager +func NewControllerClient() *ControllerClient { + return &ControllerClient{ + collectionName: "controller", + tagMeta: "controllermetadata", + } +} + +// CreateController a new collection based on the Controller +func (mc *ControllerClient) CreateController(m Controller) (Controller, error) { + + //Construct the composite key to select the entry + key := ControllerKey{ + ControllerName: m.Name, + } + + //Check if this Controller already exists + _, err := mc.GetController(m.Name) + if err == nil { + return Controller{}, pkgerrors.New("Controller already exists") + } + + err = db.DBconn.Create(mc.collectionName, key, mc.tagMeta, m) + if err != nil { + return Controller{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return m, nil +} + +// GetController returns the Controller for corresponding name +func (mc *ControllerClient) GetController(name string) (Controller, error) { + + //Construct the composite key to select the entry + key := ControllerKey{ + ControllerName: name, + } + value, err := db.DBconn.Read(mc.collectionName, key, mc.tagMeta) + if err != nil { + return Controller{}, pkgerrors.Wrap(err, "Get Controller") + } + + //value is a byte array + if value != nil { + microserv := Controller{} + err = db.DBconn.Unmarshal(value, µserv) + if err != nil { + return Controller{}, pkgerrors.Wrap(err, "Unmarshaling Value") + } + return microserv, nil + } + + return Controller{}, pkgerrors.New("Error getting Controller") +} + +// DeleteController the Controller from database +func (mc *ControllerClient) DeleteController(name string) error { + + //Construct the composite key to select the entry + key := ControllerKey{ + ControllerName: name, + } + err := db.DBconn.Delete(name, key, mc.tagMeta) + if err != nil { + return pkgerrors.Wrap(err, "Delete Controller Entry;") + } + return nil +} diff --git a/src/orchestrator/pkg/module/controller_test.go b/src/orchestrator/pkg/module/controller_test.go new file mode 100644 index 00000000..2e783c13 --- /dev/null +++ b/src/orchestrator/pkg/module/controller_test.go @@ -0,0 +1,181 @@ +/* + * 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" +) + +func TestCreateController(t *testing.T) { + testCases := []struct { + label string + inp Controller + expectedError string + mockdb *db.MockDB + expected Controller + }{ + { + label: "Create Controller", + inp: Controller{ + Name: "testController", + Host: "132.156.0.10", + Port: 8080, + }, + expected: Controller{ + Name: "testController", + Host: "132.156.0.10", + Port: 8080, + }, + expectedError: "", + mockdb: &db.MockDB{}, + }, + { + label: "Failed Create Controller", + expectedError: "Error Creating Controller", + mockdb: &db.MockDB{ + Err: pkgerrors.New("Error Creating Controller"), + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + impl := NewControllerClient() + got, err := impl.CreateController(testCase.inp) + 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 TestGetController(t *testing.T) { + + testCases := []struct { + label string + name string + expectedError string + mockdb *db.MockDB + inp string + expected Controller + }{ + { + label: "Get Controller", + name: "testController", + expected: Controller{ + Name: "testController", + Host: "132.156.0.10", + Port: 8080, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + ControllerKey{ControllerName: "testController"}.String(): { + "controllermetadata": []byte( + "{\"name\":\"testController\"," + + "\"host\":\"132.156.0.10\"," + + "\"port\":8080}"), + }, + }, + }, + }, + { + 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 := NewControllerClient() + got, err := impl.GetController(testCase.name) + 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 TestDeleteController(t *testing.T) { + + testCases := []struct { + label string + name string + expectedError string + mockdb *db.MockDB + }{ + { + label: "Delete Controller", + name: "testController", + mockdb: &db.MockDB{}, + }, + { + 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 := NewControllerClient() + err := impl.DeleteController(testCase.name) + 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/deployment_intent_groups.go b/src/orchestrator/pkg/module/deployment_intent_groups.go new file mode 100644 index 00000000..fe622771 --- /dev/null +++ b/src/orchestrator/pkg/module/deployment_intent_groups.go @@ -0,0 +1,177 @@ +/* + * 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 ( + "encoding/json" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + "reflect" + + pkgerrors "github.com/pkg/errors" +) + +// DeploymentIntentGroup shall have 2 fields - MetaData and Spec +type DeploymentIntentGroup struct { + MetaData DepMetaData `json:"metadata"` + Spec DepSpecData `json:"spec"` +} + +// DepMetaData has Name, description, userdata1, userdata2 +type DepMetaData struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// DepSpecData has profile, version, OverrideValuesObj +type DepSpecData struct { + Profile string `json:"profile"` + Version string `json:"version"` + OverrideValuesObj []OverrideValues `json:"override-values"` +} + +// OverrideValues has appName and ValuesObj +type OverrideValues struct { + AppName string `json:"app-name"` + ValuesObj map[string]string `json:"values"` +} + +// Values has ImageRepository +// type Values struct { +// ImageRepository string `json:"imageRepository"` +// } + +// DeploymentIntentGroupManager is an interface which exposes the DeploymentIntentGroupManager functionality +type DeploymentIntentGroupManager interface { + CreateDeploymentIntentGroup(d DeploymentIntentGroup, p string, ca string, v string) (DeploymentIntentGroup, error) + GetDeploymentIntentGroup(di string, p string, ca string, v string) (DeploymentIntentGroup, error) + DeleteDeploymentIntentGroup(di string, p string, ca string, v string) error +} + +// DeploymentIntentGroupKey consists of Name of the deployment group, project name, CompositeApp name, CompositeApp version +type DeploymentIntentGroupKey struct { + Name string `json:"name"` + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + Version string `json:"version"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (dk DeploymentIntentGroupKey) String() string { + out, err := json.Marshal(dk) + if err != nil { + return "" + } + return string(out) +} + +// DeploymentIntentGroupClient implements the DeploymentIntentGroupManager interface +type DeploymentIntentGroupClient struct { + storeName string + tagMetaData string +} + +// NewDeploymentIntentGroupClient return an instance of DeploymentIntentGroupClient which implements DeploymentIntentGroupManager +func NewDeploymentIntentGroupClient() *DeploymentIntentGroupClient { + return &DeploymentIntentGroupClient{ + storeName: "orchestrator", + tagMetaData: "deploymentintentgroup", + } +} + +// CreateDeploymentIntentGroup creates an entry for a given DeploymentIntentGroup in the database. Other Input parameters for it - projectName, compositeAppName, version +func (c *DeploymentIntentGroupClient) CreateDeploymentIntentGroup(d DeploymentIntentGroup, p string, ca string, + v string) (DeploymentIntentGroup, error) { + + res, err := c.GetDeploymentIntentGroup(d.MetaData.Name, p, ca, v) + if !reflect.DeepEqual(res, DeploymentIntentGroup{}) { + return DeploymentIntentGroup{}, pkgerrors.New("AppIntent already exists") + } + + //Check if project exists + _, err = NewProjectClient().GetProject(p) + if err != nil { + return DeploymentIntentGroup{}, pkgerrors.New("Unable to find the project") + } + + //check if compositeApp exists + _, err = NewCompositeAppClient().GetCompositeApp(ca, v, p) + if err != nil { + return DeploymentIntentGroup{}, pkgerrors.New("Unable to find the composite-app") + } + + gkey := DeploymentIntentGroupKey{ + Name: d.MetaData.Name, + Project: p, + CompositeApp: ca, + Version: v, + } + + err = db.DBconn.Insert(c.storeName, gkey, nil, c.tagMetaData, d) + if err != nil { + return DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Create DB entry error") + } + + return d, nil +} + +// GetDeploymentIntentGroup returns the DeploymentIntentGroup with a given name, project, compositeApp and version of compositeApp +func (c *DeploymentIntentGroupClient) GetDeploymentIntentGroup(di string, p string, ca string, v string) (DeploymentIntentGroup, error) { + + key := DeploymentIntentGroupKey{ + Name: di, + Project: p, + CompositeApp: ca, + Version: v, + } + + result, err := db.DBconn.Find(c.storeName, key, c.tagMetaData) + if err != nil { + return DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Get DeploymentIntentGroup error") + } + + if result != nil { + d := DeploymentIntentGroup{} + err = db.DBconn.Unmarshal(result[0], &d) + if err != nil { + return DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Unmarshalling DeploymentIntentGroup") + } + return d, nil + } + + return DeploymentIntentGroup{}, pkgerrors.New("Error getting DeploymentIntentGroup") + +} + +// DeleteDeploymentIntentGroup deletes a DeploymentIntentGroup +func (c *DeploymentIntentGroupClient) DeleteDeploymentIntentGroup(di string, p string, ca string, v string) error { + k := DeploymentIntentGroupKey{ + Name: di, + Project: p, + CompositeApp: ca, + Version: v, + } + + err := db.DBconn.Remove(c.storeName, k) + if err != nil { + return pkgerrors.Wrap(err, "Delete DeploymentIntentGroup entry;") + } + return nil + +} diff --git a/src/orchestrator/pkg/module/deployment_intent_groups_test.go b/src/orchestrator/pkg/module/deployment_intent_groups_test.go new file mode 100644 index 00000000..c0876ceb --- /dev/null +++ b/src/orchestrator/pkg/module/deployment_intent_groups_test.go @@ -0,0 +1,230 @@ +/* + * 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" +) + +func TestCreateDeploymentIntentGroup(t *testing.T) { + testCases := []struct { + label string + inputDeploymentIntentGrp DeploymentIntentGroup + inputProject string + inputCompositeApp string + inputCompositeAppVersion string + expectedError string + mockdb *db.MockDB + expected DeploymentIntentGroup + }{ + { + label: "Create DeploymentIntentGroup", + inputDeploymentIntentGrp: DeploymentIntentGroup{ + MetaData: DepMetaData{ + Name: "testDeploymentIntentGroup", + Description: "DescriptionTestDeploymentIntentGroup", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: DepSpecData{ + Profile: "Testprofile", + Version: "version of deployment", + OverrideValuesObj: []OverrideValues{ + {AppName: "TestAppName", + ValuesObj: map[string]string{ + "imageRepository": "registry.hub.docker.com", + }}, + {AppName: "TestAppName", + ValuesObj: map[string]string{ + "imageRepository": "registry.hub.docker.com", + }}, + }, + }, + }, + inputProject: "testProject", + inputCompositeApp: "testCompositeApp", + inputCompositeAppVersion: "testCompositeAppVersion", + expected: DeploymentIntentGroup{ + MetaData: DepMetaData{ + Name: "testDeploymentIntentGroup", + Description: "DescriptionTestDeploymentIntentGroup", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: DepSpecData{ + Profile: "Testprofile", + Version: "version of deployment", + OverrideValuesObj: []OverrideValues{ + {AppName: "TestAppName", + ValuesObj: map[string]string{ + "imageRepository": "registry.hub.docker.com", + }}, + {AppName: "TestAppName", + ValuesObj: map[string]string{ + "imageRepository": "registry.hub.docker.com", + }}, + }, + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + ProjectKey{ProjectName: "testProject"}.String(): { + "projectmetadata": []byte( + "{\"project-name\":\"testProject\"," + + "\"description\":\"Test project for unit testing\"}"), + }, + CompositeAppKey{CompositeAppName: "testCompositeApp", + Version: "testCompositeAppVersion", Project: "testProject"}.String(): { + "compositeAppmetadata": []byte( + "{\"metadata\":{" + + "\"name\":\"testCompositeApp\"," + + "\"description\":\"description\"," + + "\"userData1\":\"user data\"," + + "\"userData2\":\"user data\"" + + "}," + + "\"spec\":{" + + "\"version\":\"version of the composite app\"}}"), + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + depIntentCli := NewDeploymentIntentGroupClient() + got, err := depIntentCli.CreateDeploymentIntentGroup(testCase.inputDeploymentIntentGrp, testCase.inputProject, testCase.inputCompositeApp, testCase.inputCompositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("CreateDeploymentIntentGroup returned an unexpected error %s, ", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("CreateDeploymentIntentGroup returned an unexpected error %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("CreateDeploymentIntentGroup returned unexpected body: got %v; "+" expected %v", got, testCase.expected) + } + } + }) + } +} + +func TestGetDeploymentIntentGroup(t *testing.T) { + testCases := []struct { + label string + inputDeploymentIntentGrp string + inputProject string + inputCompositeApp string + inputCompositeAppVersion string + expected DeploymentIntentGroup + expectedError string + mockdb *db.MockDB + }{ + { + label: "Get DeploymentIntentGroup", + inputDeploymentIntentGrp: "testDeploymentIntentGroup", + inputProject: "testProject", + inputCompositeApp: "testCompositeApp", + inputCompositeAppVersion: "testCompositeAppVersion", + expected: DeploymentIntentGroup{ + MetaData: DepMetaData{ + Name: "testDeploymentIntentGroup", + Description: "DescriptionTestDeploymentIntentGroup", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: DepSpecData{ + Profile: "Testprofile", + Version: "version of deployment", + OverrideValuesObj: []OverrideValues{ + {AppName: "TestAppName", + ValuesObj: map[string]string{ + "imageRepository": "registry.hub.docker.com", + }}, + {AppName: "TestAppName", + ValuesObj: map[string]string{ + "imageRepository": "registry.hub.docker.com", + }}, + }, + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + DeploymentIntentGroupKey{ + Name: "testDeploymentIntentGroup", + Project: "testProject", + CompositeApp: "testCompositeApp", + Version: "testCompositeAppVersion", + }.String(): { + "deploymentintentgroup": []byte( + "{\"metadata\":{\"name\":\"testDeploymentIntentGroup\"," + + "\"description\":\"DescriptionTestDeploymentIntentGroup\"," + + "\"userData1\": \"userData1\"," + + "\"userData2\": \"userData2\"}," + + "\"spec\":{\"profile\": \"Testprofile\"," + + "\"version\": \"version of deployment\"," + + "\"override-values\":[" + + "{" + + "\"app-name\": \"TestAppName\"," + + "\"values\": " + + "{" + + "\"imageRepository\":\"registry.hub.docker.com\"" + + "}" + + "}," + + "{" + + "\"app-name\": \"TestAppName\"," + + "\"values\": " + + "{" + + "\"imageRepository\":\"registry.hub.docker.com\"" + + "}" + + "}" + + "]}}"), + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + depIntentCli := NewDeploymentIntentGroupClient() + got, err := depIntentCli.GetDeploymentIntentGroup(testCase.inputDeploymentIntentGrp, testCase.inputProject, testCase.inputCompositeApp, testCase.inputCompositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("GetDeploymentIntentGroup returned an unexpected error: %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("GetDeploymentIntentGroup returned an unexpected error: %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("GetDeploymentIntentGroup returned unexpected body: got %v;"+ + " expected %v", got, testCase.expected) + } + } + }) + } +} diff --git a/src/orchestrator/pkg/module/generic_placement_intent.go b/src/orchestrator/pkg/module/generic_placement_intent.go new file mode 100644 index 00000000..8e739fc8 --- /dev/null +++ b/src/orchestrator/pkg/module/generic_placement_intent.go @@ -0,0 +1,165 @@ +/* + * 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 ( + "encoding/json" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// GenericPlacementIntent shall have 2 fields - metadata and spec +type GenericPlacementIntent struct { + MetaData GenIntentMetaData `json:"metadata"` + Spec GenIntentSpecData `json:"spec"` +} + +// GenIntentMetaData has name, description, userdata1, userdata2 +type GenIntentMetaData struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// GenIntentSpecData has logical-cloud-name +type GenIntentSpecData struct { + LogicalCloud string `json:"logical-cloud"` +} + +// GenericPlacementIntentManager is an interface which exposes the GenericPlacementIntentManager functionality +type GenericPlacementIntentManager interface { + CreateGenericPlacementIntent(g GenericPlacementIntent, p string, ca string, + v string) (GenericPlacementIntent, error) + GetGenericPlacementIntent(intentName string, projectName string, + compositeAppName string, version string) (GenericPlacementIntent, error) + DeleteGenericPlacementIntent(intentName string, projectName string, + compositeAppName string, version string) error +} + +// GenericPlacementIntentKey is used as the primary key +type GenericPlacementIntentKey struct { + Name string `json:"name"` + Project string `json:"project"` + CompositeApp string `json:"compositeapp"` + Version string `json:"version"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (gk GenericPlacementIntentKey) String() string { + out, err := json.Marshal(gk) + if err != nil { + return "" + } + return string(out) +} + +// GenericPlacementIntentClient implements the GenericPlacementIntentManager interface +type GenericPlacementIntentClient struct { + storeName string + tagMetaData string +} + +// NewGenericPlacementIntentClient return an instance of GenericPlacementIntentClient which implements GenericPlacementIntentManager +func NewGenericPlacementIntentClient() *GenericPlacementIntentClient { + return &GenericPlacementIntentClient{ + storeName: "orchestrator", + tagMetaData: "genericplacementintent", + } +} + +// CreateGenericPlacementIntent creates an entry for GenericPlacementIntent in the database. Other Input parameters for it - projectName, compositeAppName, version +func (c *GenericPlacementIntentClient) CreateGenericPlacementIntent(g GenericPlacementIntent, p string, ca string, + v string) (GenericPlacementIntent, error) { + + // check if the genericPlacement already exists. + res, err := c.GetGenericPlacementIntent(g.MetaData.Name, p, ca, v) + if res != (GenericPlacementIntent{}) { + return GenericPlacementIntent{}, pkgerrors.New("Intent already exists") + } + + //Check if project exists + _, err = NewProjectClient().GetProject(p) + if err != nil { + return GenericPlacementIntent{}, pkgerrors.New("Unable to find the project") + } + + // check if compositeApp exists + _, err = NewCompositeAppClient().GetCompositeApp(ca, v, p) + if err != nil { + return GenericPlacementIntent{}, pkgerrors.New("Unable to find the composite-app") + } + + gkey := GenericPlacementIntentKey{ + Name: g.MetaData.Name, + Project: p, + CompositeApp: ca, + Version: v, + } + + err = db.DBconn.Insert(c.storeName, gkey, nil, c.tagMetaData, g) + if err != nil { + return GenericPlacementIntent{}, pkgerrors.Wrap(err, "Create DB entry error") + } + + return g, nil +} + +// GetGenericPlacementIntent shall take arguments - name of the intent, name of the project, name of the composite app and version of the composite app. It shall return the genericPlacementIntent if its present. +func (c *GenericPlacementIntentClient) GetGenericPlacementIntent(i string, p string, ca string, v string) (GenericPlacementIntent, error) { + key := GenericPlacementIntentKey{ + Name: i, + Project: p, + CompositeApp: ca, + Version: v, + } + + result, err := db.DBconn.Find(c.storeName, key, c.tagMetaData) + if err != nil { + return GenericPlacementIntent{}, pkgerrors.Wrap(err, "Get Intent error") + } + + if result != nil { + g := GenericPlacementIntent{} + err = db.DBconn.Unmarshal(result[0], &g) + if err != nil { + return GenericPlacementIntent{}, pkgerrors.Wrap(err, "Unmarshalling GenericPlacement Intent") + } + return g, nil + } + + return GenericPlacementIntent{}, pkgerrors.New("Error getting GenericPlacementIntent") + +} + +// DeleteGenericPlacementIntent the intent from the database +func (c *GenericPlacementIntentClient) DeleteGenericPlacementIntent(i string, p string, ca string, v string) error { + key := GenericPlacementIntentKey{ + Name: i, + Project: p, + CompositeApp: ca, + Version: v, + } + + err := db.DBconn.Remove(c.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Project entry;") + } + return nil +} diff --git a/src/orchestrator/pkg/module/generic_placement_intent_test.go b/src/orchestrator/pkg/module/generic_placement_intent_test.go new file mode 100644 index 00000000..c87f9ddc --- /dev/null +++ b/src/orchestrator/pkg/module/generic_placement_intent_test.go @@ -0,0 +1,184 @@ +/* + * 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" +) + +func TestCreateGenericPlacementIntent(t *testing.T) { + testCases := []struct { + label string + inputIntent GenericPlacementIntent + inputProject string + inputCompositeApp string + inputCompositeAppVersion string + expectedError string + mockdb *db.MockDB + expected GenericPlacementIntent + }{ + { + label: "Create GenericPlacementIntent", + inputIntent: GenericPlacementIntent{ + MetaData: GenIntentMetaData{ + Name: "testGenericPlacement", + Description: " A sample intent for testing", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: GenIntentSpecData{ + LogicalCloud: "logicalCloud1", + }, + }, + inputProject: "testProject", + inputCompositeApp: "testCompositeApp", + inputCompositeAppVersion: "testCompositeAppVersion", + expected: GenericPlacementIntent{ + MetaData: GenIntentMetaData{ + Name: "testGenericPlacement", + Description: " A sample intent for testing", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: GenIntentSpecData{ + LogicalCloud: "logicalCloud1", + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + ProjectKey{ProjectName: "testProject"}.String(): { + "projectmetadata": []byte( + "{\"project-name\":\"testProject\"," + + "\"description\":\"Test project for unit testing\"}"), + }, + CompositeAppKey{CompositeAppName: "testCompositeApp", + Version: "testCompositeAppVersion", Project: "testProject"}.String(): { + "compositeAppmetadata": []byte( + "{\"metadata\":{" + + "\"name\":\"testCompositeApp\"," + + "\"description\":\"description\"," + + "\"userData1\":\"user data\"," + + "\"userData2\":\"user data\"" + + "}," + + "\"spec\":{" + + "\"version\":\"version of the composite app\"}}"), + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + intentCli := NewGenericPlacementIntentClient() + got, err := intentCli.CreateGenericPlacementIntent(testCase.inputIntent, testCase.inputProject, testCase.inputCompositeApp, testCase.inputCompositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("CreateGenericPlacementIntent returned an unexpected error %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("CreateGenericPlacementIntent returned an unexpected error %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("CreateGenericPlacementIntent returned unexpected body: got %v; "+" expected %v", got, testCase.expected) + } + } + }) + + } +} + +func TestGetGenericPlacementIntent(t *testing.T) { + + testCases := []struct { + label string + expectedError string + expected GenericPlacementIntent + mockdb *db.MockDB + intentName string + projectName string + compositeAppName string + compositeAppVersion string + }{ + { + label: "Get Intent", + intentName: "testIntent", + projectName: "testProject", + compositeAppName: "testCompositeApp", + compositeAppVersion: "testVersion", + expected: GenericPlacementIntent{ + MetaData: GenIntentMetaData{ + Name: "testIntent", + Description: "A sample intent for testing", + UserData1: "userData1", + UserData2: "userData2", + }, + Spec: GenIntentSpecData{ + LogicalCloud: "logicalCloud1", + }, + }, + expectedError: "", + mockdb: &db.MockDB{ + Items: map[string]map[string][]byte{ + GenericPlacementIntentKey{ + Name: "testIntent", + Project: "testProject", + CompositeApp: "testCompositeApp", + Version: "testVersion", + }.String(): { + "genericplacementintent": []byte( + "{\"metadata\":{\"Name\":\"testIntent\"," + + "\"Description\":\"A sample intent for testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\": \"userData2\"}," + + "\"spec\":{\"Logical-Cloud\": \"logicalCloud1\"}}"), + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + db.DBconn = testCase.mockdb + intentCli := NewGenericPlacementIntentClient() + got, err := intentCli.GetGenericPlacementIntent(testCase.intentName, testCase.projectName, testCase.compositeAppName, testCase.compositeAppVersion) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("GetGenericPlacementIntent returned an unexpected error: %s", err) + } + if strings.Contains(err.Error(), testCase.expectedError) == false { + t.Fatalf("GetGenericPlacementIntent returned an unexpected error: %s", err) + } + } else { + if reflect.DeepEqual(testCase.expected, got) == false { + t.Errorf("GetGenericPlacementIntent returned unexpected body: got %v;"+ + " expected %v", got, testCase.expected) + } + } + + }) + } + +} diff --git a/src/orchestrator/pkg/module/module.go b/src/orchestrator/pkg/module/module.go index e4482098..8f2948dd 100644 --- a/src/orchestrator/pkg/module/module.go +++ b/src/orchestrator/pkg/module/module.go @@ -16,19 +16,34 @@ 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 + Controller *ControllerClient + Cluster *ClusterClient + GenericPlacementIntent *GenericPlacementIntentClient + AppIntent *AppIntentClient + DeploymentIntentGroup *DeploymentIntentGroupClient + Intent *IntentClient + CompositeProfile *CompositeProfileClient + AppProfile *AppProfileClient + // 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() + c.Controller = NewControllerClient() + c.Cluster = NewClusterClient() + c.GenericPlacementIntent = NewGenericPlacementIntentClient() + c.AppIntent = NewAppIntentClient() + c.DeploymentIntentGroup = NewDeploymentIntentGroupClient() + c.Intent = NewIntentClient() + c.CompositeProfile = NewCompositeProfileClient() + c.AppProfile = NewAppProfileClient() + // Add Client API handlers here + return c +} diff --git a/src/orchestrator/pkg/module/project.go b/src/orchestrator/pkg/module/project.go index e44164f9..a95251b5 100644 --- a/src/orchestrator/pkg/module/project.go +++ b/src/orchestrator/pkg/module/project.go @@ -1,5 +1,5 @@ /* - * Copyright 2019 Intel Corporation, Inc + * 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. @@ -24,16 +24,22 @@ import ( pkgerrors "github.com/pkg/errors" ) -// Project contains the parameters needed for Projects -// It implements the interface for managing the Projects +// Project contains the metaData for Projects type Project struct { - ProjectName string `json:"project-name"` + MetaData ProjectMetaData `json:"metadata"` +} + +// ProjectMetaData contains the parameters for creating a project +type ProjectMetaData struct { + Name string `json:"name"` Description string `json:"description"` + 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:"rb-name"` + ProjectName string `json:"project"` } // We will use json marshalling to convert to string to @@ -47,14 +53,14 @@ func (pk ProjectKey) String() string { return string(out) } -// Manager is an interface exposes the Project functionality +// ProjectManager is an interface exposes the Project functionality type ProjectManager interface { CreateProject(pr Project) (Project, error) GetProject(name string) (Project, error) DeleteProject(name string) error } -// ProjectClient implements the Manager +// ProjectClient implements the ProjectManager // It will also be used to maintain some localized state type ProjectClient struct { storeName string @@ -62,10 +68,11 @@ type ProjectClient struct { } // NewProjectClient returns an instance of the ProjectClient -// which implements the Manager +// which implements the ProjectManager func NewProjectClient() *ProjectClient { return &ProjectClient{ - tagMeta: "projectmetadata", + storeName: "orchestrator", + tagMeta: "projectmetadata", } } @@ -74,16 +81,16 @@ func (v *ProjectClient) CreateProject(p Project) (Project, error) { //Construct the composite key to select the entry key := ProjectKey{ - ProjectName: p.ProjectName, + ProjectName: p.MetaData.Name, } //Check if this Project already exists - _, err := v.GetProject(p.ProjectName) + _, err := v.GetProject(p.MetaData.Name) if err == nil { return Project{}, pkgerrors.New("Project already exists") } - err = db.DBconn.Create(p.ProjectName, key, v.tagMeta, p) + err = db.DBconn.Create(v.storeName, key, v.tagMeta, p) if err != nil { return Project{}, pkgerrors.Wrap(err, "Creating DB Entry") } @@ -98,7 +105,7 @@ func (v *ProjectClient) GetProject(name string) (Project, error) { key := ProjectKey{ ProjectName: name, } - value, err := db.DBconn.Read(name, key, v.tagMeta) + value, err := db.DBconn.Read(v.storeName, key, v.tagMeta) if err != nil { return Project{}, pkgerrors.Wrap(err, "Get Project") } @@ -123,7 +130,7 @@ func (v *ProjectClient) DeleteProject(name string) error { key := ProjectKey{ ProjectName: name, } - err := db.DBconn.Delete(name, key, v.tagMeta) + err := db.DBconn.Delete(v.storeName, key, v.tagMeta) if err != nil { return pkgerrors.Wrap(err, "Delete Project Entry;") } diff --git a/src/orchestrator/pkg/module/project_test.go b/src/orchestrator/pkg/module/project_test.go index 7f4d9b3e..f6856f86 100644 --- a/src/orchestrator/pkg/module/project_test.go +++ b/src/orchestrator/pkg/module/project_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2018 Intel Corporation, Inc + * 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. @@ -37,12 +37,20 @@ func TestCreateProject(t *testing.T) { { label: "Create Project", inp: Project{ - ProjectName: "testProject", - Description: "A sample Project used for unit testing", + MetaData: ProjectMetaData{ + Name: "testProject", + Description: "A sample Project used for unit testing", + UserData1: "data1", + UserData2: "data2", + }, }, expected: Project{ - ProjectName: "testProject", - Description: "A sample Project used for unit testing", + MetaData: ProjectMetaData{ + Name: "testProject", + Description: "A sample Project used for unit testing", + UserData1: "data1", + UserData2: "data2", + }, }, expectedError: "", mockdb: &db.MockDB{}, @@ -92,16 +100,25 @@ func TestGetProject(t *testing.T) { label: "Get Project", name: "testProject", expected: Project{ - ProjectName: "testProject", - Description: "Test project for unit testing", + MetaData: ProjectMetaData{ + Name: "testProject", + Description: "Test project for unit testing", + UserData1: "userData1", + UserData2: "userData2", + }, }, expectedError: "", mockdb: &db.MockDB{ Items: map[string]map[string][]byte{ ProjectKey{ProjectName: "testProject"}.String(): { "projectmetadata": []byte( - "{\"project-name\":\"testProject\"," + - "\"description\":\"Test project for unit testing\"}"), + "{" + + "\"metadata\" : {" + + "\"Name\":\"testProject\"," + + "\"Description\":\"Test project for unit testing\"," + + "\"UserData1\": \"userData1\"," + + "\"UserData2\":\"userData2\"}" + + "}"), }, }, }, |