From 10b17da590fc43622c6080815f65fbbb2721b640 Mon Sep 17 00:00:00 2001 From: enyinna1234 Date: Fri, 14 Feb 2020 08:41:49 -0800 Subject: Add Distributed Cloud Manager This handles RESTful API CRUD operations for logical clouds, CLuster, User Permissions, Quota, and Key Value pairs. See: https://wiki.onap.org/x/tAiVB Issue-ID: MULTICLOUD-996 Signed-off-by: Enyinna Ochulor Change-Id: I654a304cd682f762c02cfd92b4483d1edea63fca --- src/dcm/pkg/module/cluster.go | 205 ++++++++++++++++++++ src/dcm/pkg/module/cluster_test.go | 115 ++++++++++++ src/dcm/pkg/module/keyvalue.go | 205 ++++++++++++++++++++ src/dcm/pkg/module/keyvalue_test.go | 115 ++++++++++++ src/dcm/pkg/module/logicalcloud.go | 291 +++++++++++++++++++++++++++++ src/dcm/pkg/module/logicalcloud_test.go | 157 ++++++++++++++++ src/dcm/pkg/module/module.go | 39 ++++ src/dcm/pkg/module/quota.go | 222 ++++++++++++++++++++++ src/dcm/pkg/module/quota_test.go | 115 ++++++++++++ src/dcm/pkg/module/userpermissions.go | 193 +++++++++++++++++++ src/dcm/pkg/module/userpermissions_test.go | 110 +++++++++++ 11 files changed, 1767 insertions(+) create mode 100644 src/dcm/pkg/module/cluster.go create mode 100644 src/dcm/pkg/module/cluster_test.go create mode 100644 src/dcm/pkg/module/keyvalue.go create mode 100644 src/dcm/pkg/module/keyvalue_test.go create mode 100644 src/dcm/pkg/module/logicalcloud.go create mode 100644 src/dcm/pkg/module/logicalcloud_test.go create mode 100644 src/dcm/pkg/module/module.go create mode 100644 src/dcm/pkg/module/quota.go create mode 100644 src/dcm/pkg/module/quota_test.go create mode 100644 src/dcm/pkg/module/userpermissions.go create mode 100644 src/dcm/pkg/module/userpermissions_test.go (limited to 'src/dcm/pkg') diff --git a/src/dcm/pkg/module/cluster.go b/src/dcm/pkg/module/cluster.go new file mode 100644 index 00000000..38848990 --- /dev/null +++ b/src/dcm/pkg/module/cluster.go @@ -0,0 +1,205 @@ +/* +* 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 ( + pkgerrors "github.com/pkg/errors" +) + +// Cluster contains the parameters needed for a Cluster +type Cluster struct { + MetaData ClusterMeta `json:"metadata"` + Specification ClusterSpec `json:"spec"` +} + +type ClusterMeta struct { + ClusterReference string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +type ClusterSpec struct { + ClusterProvider string `json:"cluster-provider"` + ClusterName string `json:"cluster-name"` + LoadBalancerIP string `json:"loadbalancer-ip"` +} + + +type ClusterKey struct { + Project string `json:"project"` + LogicalCloudName string `json:"logical-cloud-name"` + ClusterReference string `json:"clname"` +} + +// ClusterManager is an interface that exposes the connection +// functionality +type ClusterManager interface { + CreateCluster(project, logicalCloud string, c Cluster) (Cluster, error) + GetCluster(project, logicalCloud, name string) (Cluster, error) + GetAllClusters(project, logicalCloud string) ([]Cluster, error) + DeleteCluster(project, logicalCloud, name string) error + UpdateCluster(project, logicalCloud, name string, c Cluster) (Cluster, error) +} + +// ClusterClient implements the ClusterManager +// It will also be used to maintain some localized state +type ClusterClient struct { + storeName string + tagMeta string + util Utility +} + +// ClusterClient returns an instance of the ClusterClient +// which implements the ClusterManager +func NewClusterClient() *ClusterClient { + service := DBService{} + return &ClusterClient{ + storeName: "orchestrator", + tagMeta: "cluster", + util: service, + } +} + +// Create entry for the cluster reference resource in the database +func (v *ClusterClient) CreateCluster(project, logicalCloud string, c Cluster) (Cluster, error) { + + //Construct key consisting of name + key := ClusterKey{ + Project: project, + LogicalCloudName: logicalCloud, + ClusterReference: c.MetaData.ClusterReference, + } + + //Check if project exists + err := v.util.CheckProject(project) + if err != nil { + return Cluster{}, pkgerrors.New("Unable to find the project") + } + //check if logical cloud exists + err = v.util.CheckLogicalCloud(project, logicalCloud) + if err != nil { + return Cluster{}, pkgerrors.New("Unable to find the logical cloud") + } + //Check if this Cluster reference already exists + _, err = v.GetCluster(project, logicalCloud, c.MetaData.ClusterReference) + if err == nil { + return Cluster{}, pkgerrors.New("Cluster reference already exists") + } + + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return c, nil +} + +// Get returns Cluster for corresponding cluster reference +func (v *ClusterClient) GetCluster(project, logicalCloud, clusterReference string)(Cluster, error) { + + //Construct the composite key to select the entry + key := ClusterKey{ + Project: project, + LogicalCloudName: logicalCloud, + ClusterReference: clusterReference, + } + + value, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Get Cluster reference") + } + + //value is a byte array + if value != nil { + cl := Cluster{} + err = v.util.DBUnmarshal(value[0], &cl) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + return cl, nil + } + + return Cluster{}, pkgerrors.New("Error getting Cluster") +} + + +// GetAll returns all cluster references in the logical cloud +func (v *ClusterClient) GetAllClusters(project, logicalCloud string)([]Cluster, error) { + //Construct the composite key to select clusters + key := ClusterKey{ + Project: project, + LogicalCloudName: logicalCloud, + ClusterReference: "", + } + var resp []Cluster + values, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return []Cluster{}, pkgerrors.Wrap(err, "Get All Cluster references") + } + + for _, value := range values { + cl := Cluster{} + err = v.util.DBUnmarshal(value, &cl) + if err != nil { + return []Cluster{}, pkgerrors.Wrap(err, "Unmarshaling values") + } + resp = append(resp, cl) + } + + return resp, nil +} + +// Delete the Cluster reference entry from database +func (v *ClusterClient) DeleteCluster(project, logicalCloud, clusterReference string) error { + //Construct the composite key to select the entry + key := ClusterKey{ + Project: project, + LogicalCloudName: logicalCloud, + ClusterReference: clusterReference, + } + err := v.util.DBRemove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Cluster Reference") + } + return nil +} + +// Update an entry for the Cluster reference in the database +func (v *ClusterClient) UpdateCluster(project, logicalCloud, clusterReference string, c Cluster) (Cluster, error) { + + key := ClusterKey{ + Project: project, + LogicalCloudName: logicalCloud, + ClusterReference: clusterReference, + } + + //Check for name mismatch in cluster reference + if c.MetaData.ClusterReference != clusterReference { + return Cluster{}, pkgerrors.New("Update Error - Cluster reference mismatch") + } + //Check if this Cluster reference exists + _, err := v.GetCluster(project, logicalCloud, clusterReference) + if err != nil { + return Cluster{}, pkgerrors.New("Update Error - Cluster reference doesn't exist") + } + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return Cluster{}, pkgerrors.Wrap(err, "Updating DB Entry") + } + return c, nil +} diff --git a/src/dcm/pkg/module/cluster_test.go b/src/dcm/pkg/module/cluster_test.go new file mode 100644 index 00000000..d42935db --- /dev/null +++ b/src/dcm/pkg/module/cluster_test.go @@ -0,0 +1,115 @@ +package module + +import ( + "testing" + + "github.com/pkg/errors" + +) + + +func TestCreateCluster(t *testing.T) { + + mData := ClusterMeta{ + ClusterReference: "test_cluster", + } + + cl := Cluster { + MetaData: mData, + } + data1 := [][]byte{} + + + key := ClusterKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + ClusterReference: "test_cluster", + } + myMocks := new(mockValues) + // just to get an error value + err1 := errors.New("math: square root of negative number") + + myMocks.On("CheckProject", "test_project").Return(nil) + myMocks.On("CheckLogicalCloud", "test_project", "test_asdf").Return(nil) + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", cl).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, err1) + + clClient := ClusterClient{"test_dcm", "test_meta", myMocks} + _, err := clClient.CreateCluster("test_project", "test_asdf", cl) + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestGetCluster(t *testing.T) { + key := ClusterKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + ClusterReference: "test_cluster", + } + + data1 := [][]byte{ + []byte("abc"), + } + + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + clClient := ClusterClient{"test_dcm", "test_meta", myMocks} + _, err := clClient.GetCluster("test_project", "test_asdf", "test_cluster") + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestDeleteCluster(t *testing.T) { + + key := ClusterKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + ClusterReference: "test_cluster", + } + + myMocks := new(mockValues) + + myMocks.On("DBRemove", "test_dcm", key).Return(nil) + + clClient := ClusterClient{"test_dcm", "test_meta", myMocks} + err := clClient.DeleteCluster("test_project", "test_asdf", "test_cluster") + if err != nil { + t.Errorf("Some error occured!") + } + +} + +func TestUpdateCluster(t *testing.T) { + key := ClusterKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + ClusterReference: "test_cluster", + } + mData := ClusterMeta{ + ClusterReference: "test_cluster", + } + cl := Cluster{ + MetaData: mData, + } + data1 := [][]byte{ + []byte("abc"), + } + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", cl).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + clClient := ClusterClient{"test_dcm", "test_meta", myMocks} + _, err := clClient.UpdateCluster("test_project", "test_asdf", "test_cluster", cl) + if err != nil { + t.Errorf("Some error occured!") + } +} diff --git a/src/dcm/pkg/module/keyvalue.go b/src/dcm/pkg/module/keyvalue.go new file mode 100644 index 00000000..4e3e0fab --- /dev/null +++ b/src/dcm/pkg/module/keyvalue.go @@ -0,0 +1,205 @@ +/* +* 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 ( + pkgerrors "github.com/pkg/errors" +) + +// KeyValue contains the parameters needed for a key value +type KeyValue struct { + MetaData KVMetaDataList `json:"metadata"` + Specification KVSpec `json:"spec"` +} + + +// MetaData contains the parameters needed for metadata +type KVMetaDataList struct { + KeyValueName string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// Spec contains the parameters needed for spec +type KVSpec struct { + Kv []map[string]interface{} `json:"kv"` +} + +// KeyValueKey is the key structure that is used in the database +type KeyValueKey struct { + Project string `json:"project"` + LogicalCloudName string `json:"logical-cloud-name"` + KeyValueName string `json:"kvname"` +} + +// KeyValueManager is an interface that exposes the connection +// functionality +type KeyValueManager interface { + CreateKVPair(project, logicalCloud string, c KeyValue) (KeyValue, error) + GetKVPair(project, logicalCloud, name string) (KeyValue, error) + GetAllKVPairs(project, logicalCloud string) ([]KeyValue, error) + DeleteKVPair(project, logicalCloud, name string) error + UpdateKVPair(project, logicalCloud, name string, c KeyValue) (KeyValue, error) +} + +// KeyValueClient implements the KeyValueManager +// It will also be used to maintain some localized state +type KeyValueClient struct { + storeName string + tagMeta string + util Utility +} + +// KeyValueClient returns an instance of the KeyValueClient +// which implements the KeyValueManager +func NewKeyValueClient() *KeyValueClient { + service := DBService{} + return &KeyValueClient{ + storeName: "orchestrator", + tagMeta: "keyvalue", + util: service, + } +} + +// Create entry for the key value resource in the database +func (v *KeyValueClient) CreateKVPair(project, logicalCloud string, c KeyValue) (KeyValue, error) { + + //Construct key consisting of name + key := KeyValueKey{ + Project: project, + LogicalCloudName: logicalCloud, + KeyValueName: c.MetaData.KeyValueName, + } + + //Check if project exist + err := v.util.CheckProject(project) + if err != nil { + return KeyValue{}, pkgerrors.New("Unable to find the project") + } + //check if logical cloud exists + err = v.util.CheckLogicalCloud(project, logicalCloud) + if err != nil { + return KeyValue{}, pkgerrors.New("Unable to find the logical cloud") + } + //Check if this Key Value already exists + _, err = v.GetKVPair(project, logicalCloud, c.MetaData.KeyValueName) + if err == nil { + return KeyValue{}, pkgerrors.New("Key Value already exists") + } + + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return KeyValue{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return c, nil +} + +// Get returns Key Value for correspondin name +func (v *KeyValueClient) GetKVPair(project, logicalCloud, kvPairName string) (KeyValue, error) { + + //Construct the composite key to select the entry + key := KeyValueKey{ + Project: project, + LogicalCloudName: logicalCloud, + KeyValueName: kvPairName, + } + value, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return KeyValue{}, pkgerrors.Wrap(err, "Get Key Value") + } + + //value is a byte array + if value != nil { + kv := KeyValue{} + err = v.util.DBUnmarshal(value[0], &kv) + if err != nil { + return KeyValue{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + return kv, nil + } + + return KeyValue{}, pkgerrors.New("Error getting Key Value") +} + +// Get All lists all key value pairs +func (v *KeyValueClient) GetAllKVPairs(project, logicalCloud string) ([]KeyValue, error) { + + //Construct the composite key to select the entry + key := KeyValueKey{ + Project: project, + LogicalCloudName: logicalCloud, + KeyValueName: "", + } + var resp []KeyValue + values, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return []KeyValue{}, pkgerrors.Wrap(err, "Get Key Value") + } + + for _, value := range values { + kv := KeyValue{} + err = v.util.DBUnmarshal(value, &kv) + if err != nil { + return []KeyValue{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + resp = append(resp, kv) + } + + return resp, nil +} + +// Delete the Key Value entry from database +func (v *KeyValueClient) DeleteKVPair(project, logicalCloud, kvPairName string) error { + + //Construct the composite key to select the entry + key := KeyValueKey{ + Project: project, + LogicalCloudName: logicalCloud, + KeyValueName: kvPairName, + } + err := v.util.DBRemove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Key Value") + } + return nil +} + +// Update an entry for the Key Value in the database +func (v *KeyValueClient) UpdateKVPair(project, logicalCloud, kvPairName string, c KeyValue) (KeyValue, error) { + + key := KeyValueKey{ + Project: project, + LogicalCloudName: logicalCloud, + KeyValueName: kvPairName, + } + //Check if KV pair URl name is the same name in json + if c.MetaData.KeyValueName != kvPairName { + return KeyValue{}, pkgerrors.New("Update Error - KV pair name mismatch") + } + //Check if this Key Value exists + _, err := v.GetKVPair(project, logicalCloud, kvPairName) + if err != nil { + return KeyValue{}, pkgerrors.New("Update Error - Key Value Pair doesn't exist") + } + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return KeyValue{}, pkgerrors.Wrap(err, "Updating DB Entry") + } + return c, nil +} diff --git a/src/dcm/pkg/module/keyvalue_test.go b/src/dcm/pkg/module/keyvalue_test.go new file mode 100644 index 00000000..9faceda4 --- /dev/null +++ b/src/dcm/pkg/module/keyvalue_test.go @@ -0,0 +1,115 @@ +package module + +import ( + "testing" + + "github.com/pkg/errors" + +) + + +func TestCreateKVPair(t *testing.T) { + + mData := KVMetaDataList{ + KeyValueName: "test_kv_pair", + } + + kv := KeyValue { + MetaData: mData, + } + data1 := [][]byte{} + + + key := KeyValueKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + KeyValueName: "test_kv_pair", + } + myMocks := new(mockValues) + // just to get an error value + err1 := errors.New("math: square root of negative number") + + myMocks.On("CheckProject", "test_project").Return(nil) + myMocks.On("CheckLogicalCloud", "test_project", "test_asdf").Return(nil) + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", kv).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, err1) + + kvClient := KeyValueClient{"test_dcm", "test_meta", myMocks} + _, err := kvClient.CreateKVPair("test_project", "test_asdf", kv) + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestGetKVPair(t *testing.T) { + key := KeyValueKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + KeyValueName: "test_kv_pair", + } + + data1 := [][]byte{ + []byte("abc"), + } + + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + kvClient := KeyValueClient{"test_dcm", "test_meta", myMocks} + _, err := kvClient.GetKVPair("test_project", "test_asdf", "test_kv_pair") + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestDeleteKVPair(t *testing.T) { + + key := KeyValueKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + KeyValueName: "test_kv_pair", + } + + myMocks := new(mockValues) + + myMocks.On("DBRemove", "test_dcm", key).Return(nil) + + kvClient := KeyValueClient{"test_dcm", "test_meta", myMocks} + err := kvClient.DeleteKVPair("test_project", "test_asdf", "test_kv_pair") + if err != nil { + t.Errorf("Some error occured!") + } + +} + +func TestUpdateKVPair(t *testing.T) { + key := KeyValueKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + KeyValueName: "test_kv_pair", + } + mData := KVMetaDataList{ + KeyValueName: "test_kv_pair", + } + kv := KeyValue{ + MetaData: mData, + } + data1 := [][]byte{ + []byte("abc"), + } + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", kv).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + kvClient := KeyValueClient{"test_dcm", "test_meta", myMocks} + _, err := kvClient.UpdateKVPair("test_project", "test_asdf", "test_kv_pair", kv) + if err != nil { + t.Errorf("Some error occured!") + } +} diff --git a/src/dcm/pkg/module/logicalcloud.go b/src/dcm/pkg/module/logicalcloud.go new file mode 100644 index 00000000..9fb1b6fb --- /dev/null +++ b/src/dcm/pkg/module/logicalcloud.go @@ -0,0 +1,291 @@ +/* +* 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" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module" + + pkgerrors "github.com/pkg/errors" +) + +// LogicalCloud contains the parameters needed for a Logical Cloud +type LogicalCloud struct { + MetaData MetaDataList `json:"metadata"` + Specification Spec `json:"spec"` +} + + +// MetaData contains the parameters needed for metadata +type MetaDataList struct { + LogicalCloudName string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +// Spec contains the parameters needed for spec +type Spec struct { + NameSpace string `json:"namespace"` + User UserData `json:"user"` + +} + +// UserData contains the parameters needed for user +type UserData struct { + UserName string `json:"user-name"` + Type string `json:"type"` + UserPermissions []UserPerm `json:"user-permissions"` +} + +// UserPerm contains the parameters needed for user permissions +type UserPerm struct { + PermName string `json:"permission-name"` + APIGroups []string `json:"apiGroups"` + Resources []string `json:"resources"` + Verbs []string `json:"verbs"` +} + +// LogicalCloudKey is the key structure that is used in the database +type LogicalCloudKey struct { + Project string `json:"project"` + LogicalCloudName string `json:"logical-cloud-name"` +} + +// LogicalCloudManager is an interface that exposes the connection +// functionality +type LogicalCloudManager interface { + Create(project string, c LogicalCloud) (LogicalCloud, error) + Get(project, name string) (LogicalCloud, error) + GetAll(project string) ([]LogicalCloud, error) + Delete(project, name string) error + Update(project, name string, c LogicalCloud) (LogicalCloud, error) + +} + +// Interface facilitates unit testing by mocking functions +type Utility interface { + DBInsert(storeName string, key db.Key, query interface{}, meta string, c interface{}) error + DBFind(storeName string, key db.Key, meta string) ([][]byte, error) + DBUnmarshal(value []byte, out interface{}) error + DBRemove(storeName string, key db.Key) error + CheckProject(project string) error + CheckLogicalCloud(project, logicalCloud string) error +} + +// LogicalCloudClient implements the LogicalCloudManager +// It will also be used to maintain some localized state +type LogicalCloudClient struct { + storeName string + tagMeta string + util Utility +} + +// Added for unit testing; implements Utility interface +type DBService struct {} + +// LogicalCloudClient returns an instance of the LogicalCloudClient +// which implements the LogicalCloudManager +func NewLogicalCloudClient() *LogicalCloudClient { + service := DBService{} + return &LogicalCloudClient{ + storeName: "orchestrator", + tagMeta: "logicalcloud", + util: service, + } +} + +// Create entry for the logical cloud resource in the database +func (v *LogicalCloudClient) Create(project string, c LogicalCloud) (LogicalCloud, error) { + + //Construct key consisting of name + key := LogicalCloudKey{ + Project: project, + LogicalCloudName: c.MetaData.LogicalCloudName, + } + + //Check if project exists + err := v.util.CheckProject(project) + if err != nil { + return LogicalCloud{}, pkgerrors.New("Unable to find the project") + } + + //Check if this Logical Cloud already exists + _, err = v.Get(project, c.MetaData.LogicalCloudName) + if err == nil { + return LogicalCloud{}, pkgerrors.New("Logical Cloud already exists") + } + + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return LogicalCloud{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return c, nil +} + +// Get returns Logical Cloud corresponding to logical cloud name +func (v *LogicalCloudClient) Get(project, logicalCloudName string) (LogicalCloud, error) { + + //Construct the composite key to select the entry + key := LogicalCloudKey{ + Project: project, + LogicalCloudName: logicalCloudName, + } + value, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return LogicalCloud{}, pkgerrors.Wrap(err, "Get Logical Cloud") + } + + //value is a byte array + if value != nil { + lc := LogicalCloud{} + err = v.util.DBUnmarshal(value[0], &lc) + if err != nil { + return LogicalCloud{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + return lc, nil + } + + return LogicalCloud{}, pkgerrors.New("Error getting Logical Cloud") +} + +// GetAll returns Logical Clouds in the project +func (v *LogicalCloudClient) GetAll(project string) ([]LogicalCloud, error) { + + //Construct the composite key to select the entry + key := LogicalCloudKey{ + Project: project, + LogicalCloudName: "", + } + + var resp []LogicalCloud + values, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return []LogicalCloud{}, pkgerrors.Wrap(err, "Get Logical Clouds") + } + + for _, value := range values { + lc := LogicalCloud{} + err = v.util.DBUnmarshal(value, &lc) + if err != nil { + return []LogicalCloud{}, pkgerrors.Wrap(err, "Unmarshaling values") + } + resp = append(resp, lc) + } + + return resp, nil +} + +// Delete the Logical Cloud entry from database +func (v *LogicalCloudClient) Delete(project, logicalCloudName string) error { + + //Construct the composite key to select the entry + key := LogicalCloudKey{ + Project: project, + LogicalCloudName: logicalCloudName, + } + err := v.util.DBRemove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Logical Cloud") + } + + return nil +} + +// Update an entry for the Logical Cloud in the database +func (v *LogicalCloudClient) Update(project, logicalCloudName string, c LogicalCloud) (LogicalCloud, error) { + + key := LogicalCloudKey{ + Project: project, + LogicalCloudName: logicalCloudName, + } + // Check for mismatch, logicalCloudName and payload logical cloud name + if c.MetaData.LogicalCloudName != logicalCloudName { + return LogicalCloud{}, pkgerrors.New("Update Error - Logical Cloud name mismatch") + } + //Check if this Logical Cloud exists + _, err := v.Get(project, logicalCloudName) + if err != nil { + return LogicalCloud{}, pkgerrors.New("Update Error - Logical Cloud doesn't exist") + } + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return LogicalCloud{}, pkgerrors.Wrap(err, "Updating DB Entry") + } + return c, nil +} + +func (d DBService) DBInsert(storeName string, key db.Key, query interface{}, meta string, c interface{}) error { + + err := db.DBconn.Insert(storeName, key, nil, meta, c) + if err != nil { + return pkgerrors.Wrap(err, "Creating DB Entry") + } + + return nil +} + +func (d DBService) DBFind(storeName string, key db.Key, meta string) ([][]byte, error) { + + value, err := db.DBconn.Find(storeName, key, meta) + if err != nil { + return [][]byte{}, pkgerrors.Wrap(err, "Get Resource") + } + + return value, nil +} + +func (d DBService) DBUnmarshal(value []byte, out interface{}) error { + + err := db.DBconn.Unmarshal(value, out) + if err != nil { + return pkgerrors.Wrap(err, "Unmarshaling Value") + } + + return nil +} + +func (d DBService) DBRemove(storeName string, key db.Key) error { + + err := db.DBconn.Remove(storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Resource") + } + + return nil +} + +func (d DBService) CheckProject(project string) error { + // Check if project exists + _, err := module.NewProjectClient().GetProject(project) + if err != nil { + return pkgerrors.New("Unable to find the project") + } + + return nil +} + +func (d DBService) CheckLogicalCloud(project, logicalCloud string) error { + // Check if logical cloud exists + _, err := NewLogicalCloudClient().Get(project, logicalCloud) + if err != nil { + return pkgerrors.New("Unable to find the logical cloud") + } + + return nil +} diff --git a/src/dcm/pkg/module/logicalcloud_test.go b/src/dcm/pkg/module/logicalcloud_test.go new file mode 100644 index 00000000..882cc292 --- /dev/null +++ b/src/dcm/pkg/module/logicalcloud_test.go @@ -0,0 +1,157 @@ +package module + +import ( + "fmt" + "testing" + + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + "github.com/stretchr/testify/mock" + "github.com/pkg/errors" + +) + +type mockValues struct { + mock.Mock +} + +func (m *mockValues) DBInsert(name string, key db.Key, query interface {}, meta string, c interface {}) error{ + fmt.Println("Mocked Insert operation in Mongo") + args := m.Called(name, key, nil, meta, c) + + return args.Error(0) +} + +func (m *mockValues) DBFind(name string, key db.Key, meta string) ([][]byte, error) { + fmt.Println("Mocked Mongo DB Find Operation") + args := m.Called(name, key, meta) + + return args.Get(0).([][]byte), args.Error(1) +} + +func (m *mockValues) DBUnmarshal(value []byte, out interface{}) error { + fmt.Println("Mocked Mongo DB Unmarshal Operation") + args := m.Called(value) + + return args.Error(0) +} + +func (m *mockValues) DBRemove(name string, key db.Key) error { + fmt.Println("Mocked Mongo DB Remove operation") + args := m.Called(name, key) + + return args.Error(0) +} + +func (m *mockValues) CheckProject(project string) error { + fmt.Println("Mocked Check Project exists") + args := m.Called(project) + + return args.Error(0) +} + +func (m *mockValues) CheckLogicalCloud(project, logicalCloud string) error { + fmt.Println("Mocked Check Logical Cloud exists") + args := m.Called(project, logicalCloud) + + return args.Error(0) +} + +func TestCreateLogicalCloud(t *testing.T) { + + mData := MetaDataList{ + LogicalCloudName: "test_asdf", + } + + lc := LogicalCloud { + MetaData: mData, + } + data1 := [][]byte{} + + key := LogicalCloudKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + } + myMocks := new(mockValues) + // just to get an error value + err1 := errors.New("math: square root of negative number") + + myMocks.On("CheckProject", "test_project").Return(nil) + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", lc).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, err1) + + lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + _, err := lcClient.Create("test_project", lc) + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestGetLogicalCloud(t *testing.T) { + key := LogicalCloudKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + } + + data1 := [][]byte{ + []byte("abc"), + } + + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + _, err := lcClient.Get("test_project", "test_asdf") + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestDeleteLogicalCloud(t *testing.T) { + + key := LogicalCloudKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + } + + myMocks := new(mockValues) + + myMocks.On("DBRemove", "test_dcm", key).Return(nil) + + lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + err := lcClient.Delete("test_project", "test_asdf") + if err != nil { + t.Errorf("Some error occured!") + } + +} + +func TestUpdateLogicalCloud(t *testing.T) { + key := LogicalCloudKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + } + mData := MetaDataList{ + LogicalCloudName: "test_asdf", + } + lc := LogicalCloud{ + MetaData: mData, + } + data1 := [][]byte{ + []byte("abc"), + } + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", lc).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + _, err := lcClient.Update("test_project", "test_asdf", lc) + if err != nil { + t.Errorf("Some error occured!") + } +} diff --git a/src/dcm/pkg/module/module.go b/src/dcm/pkg/module/module.go new file mode 100644 index 00000000..293f6dd5 --- /dev/null +++ b/src/dcm/pkg/module/module.go @@ -0,0 +1,39 @@ +/* + * 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 + +// Client for using the services in the orchestrator +type Client struct { + LogicalCloud *LogicalCloudClient + Cluster *ClusterClient + Quota *QuotaClient + UserPermission *UserPermissionClient + KeyValue *KeyValueClient + // Add Clients for API's here +} + +// NewClient creates a new client for using the services +func NewClient() *Client { + c := &Client{} + c.LogicalCloud = NewLogicalCloudClient() + c.Cluster = NewClusterClient() + c.Quota = NewQuotaClient() + c.UserPermission = NewUserPermissionClient() + c.KeyValue = NewKeyValueClient() + // Add Client API handlers here + return c +} diff --git a/src/dcm/pkg/module/quota.go b/src/dcm/pkg/module/quota.go new file mode 100644 index 00000000..1a7012f6 --- /dev/null +++ b/src/dcm/pkg/module/quota.go @@ -0,0 +1,222 @@ +/* +* 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 ( + pkgerrors "github.com/pkg/errors" +) + +// Quota contains the parameters needed for a Quota +type Quota struct { + MetaData QMetaDataList `json:"metadata"` + Specification QSpec `json:"spec"` +} + + +// MetaData contains the parameters needed for metadata +type QMetaDataList struct { + QuotaName string `json:"name"` + Description string `json:"description"` +} + +// Spec contains the parameters needed for spec +type QSpec struct { + LimitsCPU string `json:"limits.cpu"` + LimitsMemory string `json:"limits.memory"` + RequestsCPU string `json:"requests.cpu"` + RequestsMemory string `json:"requests.memory"` + RequestsStorage string `json:"requests.storage"` + LimitsEphemeralStorage string `json:"limits.ephemeral.storage"` + PersistentVolumeClaims string `json:"persistentvolumeclaims"` + Pods string `json:"pods"` + ConfigMaps string `json:"configmaps"` + ReplicationControllers string `json:"replicationcontrollers"` + ResourceQuotas string `json:"resourcequotas"` + Services string `json:"services"` + ServicesLoadBalancers string `json:"services.loadbalancers"` + ServicesNodePorts string `json:"services.nodeports"` + Secrets string `json:"secrets"` + CountReplicationControllers string `json:"count/replicationcontrollers"` + CountDeploymentsApps string `json:"count/deployments.apps"` + CountReplicasetsApps string `json:"count/replicasets.apps"` + CountStatefulSets string `json:"count/statefulsets.apps"` + CountJobsBatch string `json:"count/jobs.batch"` + CountCronJobsBatch string `json:"count/cronjobs.batch"` + CountDeploymentsExtensions string `json:"count/deployments.extensions"` +} + +// QuotaKey is the key structure that is used in the database +type QuotaKey struct { + Project string `json:"project"` + LogicalCloudName string `json:"logical-cloud-name"` + QuotaName string `json:"qname"` +} + +// QuotaManager is an interface that exposes the connection +// functionality +type QuotaManager interface { + CreateQuota(project, logicalCloud string, c Quota) (Quota, error) + GetQuota(project, logicalCloud, name string) (Quota, error) + GetAllQuotas(project, logicalCloud string) ([]Quota, error) + DeleteQuota(project, logicalCloud, name string) error + UpdateQuota(project, logicalCloud, name string, c Quota) (Quota, error) +} + +// QuotaClient implements the QuotaManager +// It will also be used to maintain some localized state +type QuotaClient struct { + storeName string + tagMeta string + util Utility +} + +// QuotaClient returns an instance of the QuotaClient +// which implements the QuotaManager +func NewQuotaClient() *QuotaClient { + service := DBService{} + return &QuotaClient{ + storeName: "orchestrator", + tagMeta: "quota", + util: service, + } +} + +// Create entry for the quota resource in the database +func (v *QuotaClient) CreateQuota(project, logicalCloud string, c Quota) (Quota, error) { + + //Construct key consisting of name + key := QuotaKey{ + Project: project, + LogicalCloudName: logicalCloud, + QuotaName: c.MetaData.QuotaName, + } + + //Check if project exists + err := v.util.CheckProject(project) + if err != nil { + return Quota{}, pkgerrors.New("Unable to find the project") + } + //check if logical cloud exists + err = v.util.CheckLogicalCloud(project, logicalCloud) + if err != nil { + return Quota{}, pkgerrors.New("Unable to find the logical cloud") + } + //Check if this Quota already exists + _, err = v.GetQuota(project, logicalCloud, c.MetaData.QuotaName) + if err == nil { + return Quota{}, pkgerrors.New("Quota already exists") + } + + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return Quota{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return c, nil +} + +// Get returns Quota for corresponding quota name +func (v *QuotaClient) GetQuota(project, logicalCloud, quotaName string) (Quota, error) { + + //Construct the composite key to select the entry + key := QuotaKey{ + Project: project, + LogicalCloudName: logicalCloud, + QuotaName: quotaName, + } + value, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return Quota{}, pkgerrors.Wrap(err, "Quota") + } + + //value is a byte array + if value != nil { + q := Quota{} + err = v.util.DBUnmarshal(value[0], &q) + if err != nil { + return Quota{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + return q, nil + } + + return Quota{}, pkgerrors.New("Error getting Quota") +} + +// GetAll returns all cluster quotas in the logical cloud +func (v *QuotaClient) GetAllQuotas(project, logicalCloud string) ([]Quota, error) { + //Construct the composite key to select the entry + key := QuotaKey{ + Project: project, + LogicalCloudName: logicalCloud, + QuotaName: "", + } + var resp []Quota + values, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return []Quota{}, pkgerrors.Wrap(err, "Get All Quotas") + } + + for _, value := range values { + q := Quota{} + err = v.util.DBUnmarshal(value, &q) + if err != nil { + return []Quota{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + resp = append(resp, q) + } + + return resp, nil +} + +// Delete the Quota entry from database +func (v *QuotaClient) DeleteQuota(project, logicalCloud, quotaName string) error { + //Construct the composite key to select the entry + key := QuotaKey{ + Project: project, + LogicalCloudName: logicalCloud, + QuotaName: quotaName, + } + err := v.util.DBRemove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Quota") + } + return nil +} + +// Update an entry for the Quota in the database +func (v *QuotaClient) UpdateQuota(project, logicalCloud, quotaName string, c Quota) (Quota, error) { + + key := QuotaKey{ + Project: project, + LogicalCloudName: logicalCloud, + QuotaName: quotaName, + } + //Check quota URL name against the quota json name + if c.MetaData.QuotaName != quotaName { + return Quota{}, pkgerrors.New("Update Error - Quota name mismatch") + } + //Check if this Quota exists + _, err := v.GetQuota(project, logicalCloud, quotaName) + if err != nil { + return Quota{}, pkgerrors.New("Update Error - Quota doesn't exist") + } + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return Quota{}, pkgerrors.Wrap(err, "Updating DB Entry") + } + return c, nil +} diff --git a/src/dcm/pkg/module/quota_test.go b/src/dcm/pkg/module/quota_test.go new file mode 100644 index 00000000..87a60d71 --- /dev/null +++ b/src/dcm/pkg/module/quota_test.go @@ -0,0 +1,115 @@ +package module + +import ( + "testing" + + "github.com/pkg/errors" + +) + + +func TestCreateQuota(t *testing.T) { + + mData := QMetaDataList{ + QuotaName: "test_quota", + } + + q := Quota { + MetaData: mData, + } + data1 := [][]byte{} + + + key := QuotaKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + QuotaName: "test_quota", + } + myMocks := new(mockValues) + // just to get an error value + err1 := errors.New("math: square root of negative number") + + myMocks.On("CheckProject", "test_project").Return(nil) + myMocks.On("CheckLogicalCloud", "test_project", "test_asdf").Return(nil) + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", q).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, err1) + + qClient := QuotaClient{"test_dcm", "test_meta", myMocks} + _, err := qClient.CreateQuota("test_project", "test_asdf", q) + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestGetQuota(t *testing.T) { + key := QuotaKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + QuotaName: "test_quota", + } + + data1 := [][]byte{ + []byte("abc"), + } + + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + qClient := QuotaClient{"test_dcm", "test_meta", myMocks} + _, err := qClient.GetQuota("test_project", "test_asdf", "test_quota") + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestDeleteQuota(t *testing.T) { + + key := QuotaKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + QuotaName: "test_quota", + } + + myMocks := new(mockValues) + + myMocks.On("DBRemove", "test_dcm", key).Return(nil) + + qClient := QuotaClient{"test_dcm", "test_meta", myMocks} + err := qClient.DeleteQuota("test_project", "test_asdf", "test_quota") + if err != nil { + t.Errorf("Some error occured!") + } + +} + +func TestUpdateQuota(t *testing.T) { + key := QuotaKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + QuotaName: "test_quota", + } + mData := QMetaDataList{ + QuotaName: "test_quota", + } + q := Quota{ + MetaData: mData, + } + data1 := [][]byte{ + []byte("abc"), + } + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", q).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + qClient := QuotaClient{"test_dcm", "test_meta", myMocks} + _, err := qClient.UpdateQuota("test_project", "test_asdf", "test_quota", q) + if err != nil { + t.Errorf("Some error occured!") + } +} diff --git a/src/dcm/pkg/module/userpermissions.go b/src/dcm/pkg/module/userpermissions.go new file mode 100644 index 00000000..cf961a65 --- /dev/null +++ b/src/dcm/pkg/module/userpermissions.go @@ -0,0 +1,193 @@ +/* +* 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 ( + pkgerrors "github.com/pkg/errors" +) + +// UserPermission contains the parameters needed for a user permission +type UserPermission struct { + UserPermissionName string `json:"name"` + APIGroups []string `json:"apiGroups"` + Resources []string `json:"resources"` + Verbs []string `json:"verbs"` +} + +// UserPermissionKey is the key structure that is used in the database +type UserPermissionKey struct { + Project string `json:"project"` + LogicalCloudName string `json:"logical-cloud-name"` + UserPermissionName string `json:"upname"` +} + +// UserPermissionManager is an interface that exposes the connection +// functionality +type UserPermissionManager interface { + CreateUserPerm(project, logicalCloud string, c UserPermission) (UserPermission, error) + GetUserPerm(project, logicalCloud, name string) (UserPermission, error) + GetAllUserPerms(project, logicalCloud string) ([]UserPermission, error) + DeleteUserPerm(project, logicalCloud, name string) error + UpdateUserPerm(project, logicalCloud, name string, c UserPermission) (UserPermission, error) +} + +// UserPermissionClient implements the UserPermissionManager +// It will also be used to maintain some localized state +type UserPermissionClient struct { + storeName string + tagMeta string + util Utility +} + +// UserPermissionClient returns an instance of the UserPermissionClient +// which implements the UserPermissionManager +func NewUserPermissionClient() *UserPermissionClient { + service := DBService{} + return &UserPermissionClient{ + storeName: "orchestrator", + tagMeta: "userpermission", + util: service, + } +} + +// Create entry for the User Permission resource in the database +func (v *UserPermissionClient) CreateUserPerm(project, logicalCloud string, c UserPermission) (UserPermission, error) { + + //Construct key consisting of name + key := UserPermissionKey { + Project: project, + LogicalCloudName: logicalCloud, + UserPermissionName: c.UserPermissionName, + } + + //Check if project exists + err := v.util.CheckProject(project) + if err != nil { + return UserPermission{}, pkgerrors.New("Unable to find the project") + } + //check if logical cloud exists + err = v.util.CheckLogicalCloud(project, logicalCloud) + if err != nil { + return UserPermission{}, pkgerrors.New("Unable to find the logical cloud") + } + + //Check if this User Permission already exists + _, err = v.GetUserPerm(project, logicalCloud, c.UserPermissionName) + if err == nil { + return UserPermission{}, pkgerrors.New("User Permission already exists") + } + + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return UserPermission{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return c, nil +} + +// Get returns User Permission for corresponding name +func (v *UserPermissionClient) GetUserPerm(project, logicalCloud, userPermName string) (UserPermission, error) { + + //Construct the composite key to select the entry + key := UserPermissionKey{ + Project: project, + LogicalCloudName: logicalCloud, + UserPermissionName: userPermName, + } + + value, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return UserPermission{}, pkgerrors.Wrap(err, "Get User Permission") + } + + //value is a byte array + if value != nil { + up := UserPermission{} + err = v.util.DBUnmarshal(value[0], &up) + if err != nil { + return UserPermission{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + return up, nil + } + + return UserPermission{}, pkgerrors.New("Error getting User Permission") +} + +// GetAll lists all user permissions +func (v *UserPermissionClient) GetAllUserPerms(project, logicalCloud string) ([]UserPermission, error) { + //Construct the composite key to select the entry + key := UserPermissionKey { + Project: project, + LogicalCloudName: logicalCloud, + UserPermissionName: "", + } + var resp []UserPermission + values, err := v.util.DBFind(v.storeName, key, v.tagMeta) + if err != nil { + return []UserPermission{}, pkgerrors.Wrap(err, "Get All User Permissions") + } + + for _, value := range values { + up := UserPermission{} + err = v.util.DBUnmarshal(value, &up) + if err != nil { + return []UserPermission{}, pkgerrors.Wrap(err, "Unmarshaling value") + } + resp = append(resp, up) + } + return resp, nil +} +// Delete the User Permission entry from database +func (v *UserPermissionClient) DeleteUserPerm(project, logicalCloud, userPermName string) error { + //Construct the composite key to select the entry + key := UserPermissionKey{ + Project: project, + LogicalCloudName: logicalCloud, + UserPermissionName: userPermName, + } + err := v.util.DBRemove(v.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete User Permission") + } + return nil +} + +// Update an entry for the User Permission in the database +func (v *UserPermissionClient) UpdateUserPerm(project, logicalCloud, userPermName string, c UserPermission) ( + UserPermission, error) { + + key := UserPermissionKey{ + Project: project, + LogicalCloudName: logicalCloud, + UserPermissionName: userPermName, + } + //Check for URL name and json permission name mismatch + if c.UserPermissionName != userPermName { + return UserPermission{}, pkgerrors.New("Update Error - Permission name mismatch") + } + //Check if this User Permission exists + _, err := v.GetUserPerm(project, logicalCloud, userPermName) + if err != nil { + return UserPermission{}, pkgerrors.New( + "Update Error - User Permission doesn't exist") + } + err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) + if err != nil { + return UserPermission{}, pkgerrors.Wrap(err, "Updating DB Entry") + } + return c, nil +} diff --git a/src/dcm/pkg/module/userpermissions_test.go b/src/dcm/pkg/module/userpermissions_test.go new file mode 100644 index 00000000..f134aa0f --- /dev/null +++ b/src/dcm/pkg/module/userpermissions_test.go @@ -0,0 +1,110 @@ +package module + +import ( + "testing" + + "github.com/pkg/errors" + +) + + +func TestCreateUserPerm(t *testing.T) { + + up := UserPermission { + UserPermissionName: "test_user_perm", + } + data1 := [][]byte{} + + // data2 := []byte("abc") + + key := UserPermissionKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + UserPermissionName: "test_user_perm", + } + myMocks := new(mockValues) + // just to get an error value + err1 := errors.New("math: square root of negative number") + + myMocks.On("CheckProject", "test_project").Return(nil) + myMocks.On("CheckLogicalCloud", "test_project", "test_asdf").Return(nil) + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", up).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, err1) + // myMocks.On("DBUnmarshal", data2).Return(nil) + + upClient := UserPermissionClient{"test_dcm", "test_meta", myMocks} + _, err := upClient.CreateUserPerm("test_project", "test_asdf", up) + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestGetUserPerm(t *testing.T) { + key := UserPermissionKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + UserPermissionName: "test_user_perm", + } + + data1 := [][]byte{ + []byte("abc"), + } + + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + upClient := UserPermissionClient{"test_dcm", "test_meta", myMocks} + _, err := upClient.GetUserPerm("test_project", "test_asdf", "test_user_perm") + if err != nil { + t.Errorf("Some error occured!") + } +} + +func TestDeleteUserPerm(t *testing.T) { + + key := UserPermissionKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + UserPermissionName: "test_user_perm", + } + + myMocks := new(mockValues) + + myMocks.On("DBRemove", "test_dcm", key).Return(nil) + + upClient := UserPermissionClient{"test_dcm", "test_meta", myMocks} + err := upClient.DeleteUserPerm("test_project", "test_asdf", "test_user_perm") + if err != nil { + t.Errorf("Some error occured!") + } + +} + +func TestUpdateUserPerm(t *testing.T) { + key := UserPermissionKey{ + Project: "test_project", + LogicalCloudName: "test_asdf", + UserPermissionName: "test_user_perm", + } + up := UserPermission{ + UserPermissionName: "test_user_perm", + } + data1 := [][]byte{ + []byte("abc"), + } + data2 := []byte("abc") + + myMocks := new(mockValues) + + myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", up).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + upClient := UserPermissionClient{"test_dcm", "test_meta", myMocks} + _, err := upClient.UpdateUserPerm("test_project", "test_asdf", "test_user_perm", up) + if err != nil { + t.Errorf("Some error occured!") + } +} -- cgit 1.2.3-korg