aboutsummaryrefslogtreecommitdiffstats
path: root/src/orchestrator/pkg/module
diff options
context:
space:
mode:
Diffstat (limited to 'src/orchestrator/pkg/module')
-rw-r--r--src/orchestrator/pkg/module/add_intents.go184
-rw-r--r--src/orchestrator/pkg/module/app_intent.go195
-rw-r--r--src/orchestrator/pkg/module/app_intent_test.go271
-rw-r--r--src/orchestrator/pkg/module/app_profile.go296
-rw-r--r--src/orchestrator/pkg/module/cluster.go540
-rw-r--r--src/orchestrator/pkg/module/composite_profile.go192
-rw-r--r--src/orchestrator/pkg/module/composite_profile_test.go175
-rw-r--r--src/orchestrator/pkg/module/compositeapp.go158
-rw-r--r--src/orchestrator/pkg/module/controller.go135
-rw-r--r--src/orchestrator/pkg/module/controller_test.go181
-rw-r--r--src/orchestrator/pkg/module/deployment_intent_groups.go177
-rw-r--r--src/orchestrator/pkg/module/deployment_intent_groups_test.go230
-rw-r--r--src/orchestrator/pkg/module/generic_placement_intent.go165
-rw-r--r--src/orchestrator/pkg/module/generic_placement_intent_test.go184
-rw-r--r--src/orchestrator/pkg/module/module.go35
-rw-r--r--src/orchestrator/pkg/module/project.go35
-rw-r--r--src/orchestrator/pkg/module/project_test.go35
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, &microserv)
+ 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\"}" +
+ "}"),
},
},
},