summaryrefslogtreecommitdiffstats
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/cluster.go540
-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.go17
11 files changed, 2277 insertions, 2 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/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/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 d03e5ffb..b46c6068 100644
--- a/src/orchestrator/pkg/module/module.go
+++ b/src/orchestrator/pkg/module/module.go
@@ -18,16 +18,29 @@ package module
// Client for using the services in the orchestrator
type Client struct {
- Project *ProjectClient
- CompositeApp *CompositeAppClient
+ Project *ProjectClient
+ CompositeApp *CompositeAppClient
+ Controller *ControllerClient
+ Cluster *ClusterClient
+ GenericPlacementIntent *GenericPlacementIntentClient
+ AppIntent *AppIntentClient
// Add Clients for API's here
+ DeploymentIntentGroup *DeploymentIntentGroupClient
+ Intent *IntentClient
}
// NewClient creates a new client for using the services
func NewClient() *Client {
c := &Client{}
+ // Add Client API handlers here
c.Project = NewProjectClient()
c.CompositeApp = NewCompositeAppClient()
+ c.Controller = NewControllerClient()
+ c.Cluster = NewClusterClient()
// Add Client API handlers here
+ c.GenericPlacementIntent = NewGenericPlacementIntentClient()
+ c.AppIntent = NewAppIntentClient()
+ c.DeploymentIntentGroup = NewDeploymentIntentGroupClient()
+ c.Intent = NewIntentClient()
return c
}