Added unit test cases for permissions cache implementation. Refactored permissions code for enabling unit tests with lower cache configuration

This commit is contained in:
vsmk98 2020-04-02 17:25:36 +08:00
parent 5031e5b8ee
commit f9677c8843
3 changed files with 109 additions and 41 deletions

View File

@ -117,19 +117,13 @@ var QIP714BlockReached = false
var networkAdminRole string var networkAdminRole string
var orgAdminRole string var orgAdminRole string
const defaultOrgMapLimit = 2000
const defaultRoleMapLimit = 2500
const defaultNodeMapLimit = 1000
const defaultAccountMapLimit = 6000
var ( var (
OrgInfoMap = NewOrgCache() OrgInfoMap *OrgCache
NodeInfoMap = NewNodeCache() NodeInfoMap *NodeCache
RoleInfoMap = NewRoleCache() RoleInfoMap *RoleCache
AcctInfoMap = NewAcctCache() AcctInfoMap *AcctCache
) )
type OrgKey struct { type OrgKey struct {
OrgId string OrgId string
} }
@ -141,17 +135,16 @@ type OrgCache struct {
populateCacheFunc func(orgId string) *OrgInfo populateCacheFunc func(orgId string) *OrgInfo
} }
func (o *OrgCache) PopulateCacheFunc(cf func(string) *OrgInfo) { func (o *OrgCache) PopulateCacheFunc(cf func(string) *OrgInfo) {
o.populateCacheFunc = cf o.populateCacheFunc = cf
} }
func NewOrgCache() *OrgCache { func NewOrgCache(cacheSize int) *OrgCache {
orgCache := OrgCache{evicted: false} orgCache := OrgCache{evicted: false}
onEvictedFunc := func(k interface{}, v interface{}) { onEvictedFunc := func(k interface{}, v interface{}) {
orgCache.evicted = true orgCache.evicted = true
} }
orgCache.c, _ = lru.NewWithEvict(defaultOrgMapLimit, onEvictedFunc) orgCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc)
return &orgCache return &orgCache
} }
@ -170,12 +163,12 @@ func (r *RoleCache) PopulateCacheFunc(cf func(*RoleKey) *RoleInfo) {
r.populateCacheFunc = cf r.populateCacheFunc = cf
} }
func NewRoleCache() *RoleCache { func NewRoleCache(cacheSize int) *RoleCache {
roleCache := RoleCache{evicted: false} roleCache := RoleCache{evicted: false}
onEvictedFunc := func(k interface{}, v interface{}) { onEvictedFunc := func(k interface{}, v interface{}) {
roleCache.evicted = true roleCache.evicted = true
} }
roleCache.c, _ = lru.NewWithEvict(defaultRoleMapLimit, onEvictedFunc) roleCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc)
return &roleCache return &roleCache
} }
@ -199,13 +192,13 @@ func (n *NodeCache) PopulateCacheFunc(cf func(string) *NodeInfo) {
n.populateCacheFunc = cf n.populateCacheFunc = cf
} }
func NewNodeCache() *NodeCache { func NewNodeCache(cacheSize int) *NodeCache {
nodeCache := NodeCache{evicted: false} nodeCache := NodeCache{evicted: false}
onEvictedFunc := func(k interface{}, v interface{}) { onEvictedFunc := func(k interface{}, v interface{}) {
nodeCache.evicted = true nodeCache.evicted = true
} }
nodeCache.c, _ = lru.NewWithEvict(defaultNodeMapLimit, onEvictedFunc) nodeCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc)
return &nodeCache return &nodeCache
} }
@ -223,13 +216,13 @@ func (a *AcctCache) PopulateCacheFunc(cf func(common.Address) *AccountInfo) {
a.populateCacheFunc = cf a.populateCacheFunc = cf
} }
func NewAcctCache() *AcctCache { func NewAcctCache(cacheSize int) *AcctCache {
acctCache := AcctCache{evicted: false} acctCache := AcctCache{evicted: false}
onEvictedFunc := func(k interface{}, v interface{}) { onEvictedFunc := func(k interface{}, v interface{}) {
acctCache.evicted = true acctCache.evicted = true
} }
acctCache.c, _ = lru.NewWithEvict(defaultAccountMapLimit, onEvictedFunc) acctCache.c, _ = lru.NewWithEvict(cacheSize, onEvictedFunc)
return &acctCache return &acctCache
} }

View File

@ -39,6 +39,13 @@ const (
NodeDelete NodeDelete
) )
const (
defaultOrgMapLimit = 2000
defaultRoleMapLimit = 2500
defaultNodeMapLimit = 1000
defaultAccountMapLimit = 6000
)
type PermissionCtrl struct { type PermissionCtrl struct {
node *node.Node node *node.Node
ethClnt bind.ContractBackend ethClnt bind.ContractBackend
@ -177,7 +184,7 @@ func (p *PermissionCtrl) AfterStart() error {
} }
// populate the initial list of permissioned nodes and account accesses // populate the initial list of permissioned nodes and account accesses
if err := p.populateInitPermissions(); err != nil { if err := p.populateInitPermissions(defaultOrgMapLimit, defaultRoleMapLimit, defaultNodeMapLimit, defaultAccountMapLimit); err != nil {
return fmt.Errorf("populateInitPermissions failed: %v", err) return fmt.Errorf("populateInitPermissions failed: %v", err)
} }
@ -313,9 +320,6 @@ func (p *PermissionCtrl) monitorQIP714Block() error {
// monitors org management related events happening via smart contracts // monitors org management related events happening via smart contracts
// and updates cache accordingly // and updates cache accordingly
func (p *PermissionCtrl) manageOrgPermissions() error { func (p *PermissionCtrl) manageOrgPermissions() error {
// set the cache function
types.OrgInfoMap.PopulateCacheFunc(p.populateOrgToCache)
chPendingApproval := make(chan *pbind.OrgManagerOrgPendingApproval, 1) chPendingApproval := make(chan *pbind.OrgManagerOrgPendingApproval, 1)
chOrgApproved := make(chan *pbind.OrgManagerOrgApproved, 1) chOrgApproved := make(chan *pbind.OrgManagerOrgApproved, 1)
chOrgSuspended := make(chan *pbind.OrgManagerOrgSuspended, 1) chOrgSuspended := make(chan *pbind.OrgManagerOrgSuspended, 1)
@ -374,10 +378,6 @@ func (p *PermissionCtrl) subscribeStopEvent() (chan stopEvent, event.Subscriptio
// Monitors node management events and updates cache accordingly // Monitors node management events and updates cache accordingly
func (p *PermissionCtrl) manageNodePermissions() error { func (p *PermissionCtrl) manageNodePermissions() error {
// populate function to populate cache
types.NodeInfoMap.PopulateValidateFunc(p.populateNodeCacheAndValidate)
types.NodeInfoMap.PopulateCacheFunc(p.populateNodeCache)
chNodeApproved := make(chan *pbind.NodeManagerNodeApproved, 1) chNodeApproved := make(chan *pbind.NodeManagerNodeApproved, 1)
chNodeProposed := make(chan *pbind.NodeManagerNodeProposed, 1) chNodeProposed := make(chan *pbind.NodeManagerNodeProposed, 1)
chNodeDeactivated := make(chan *pbind.NodeManagerNodeDeactivated, 1) chNodeDeactivated := make(chan *pbind.NodeManagerNodeDeactivated, 1)
@ -548,9 +548,6 @@ func (p *PermissionCtrl) updateDisallowedNodes(url string, operation NodeOperati
// Monitors account access related events and updates the cache accordingly // Monitors account access related events and updates the cache accordingly
func (p *PermissionCtrl) manageAccountPermissions() error { func (p *PermissionCtrl) manageAccountPermissions() error {
// set the function for cache population
types.AcctInfoMap.PopulateCacheFunc(p.populateAccountToCache)
chAccessModified := make(chan *pbind.AcctManagerAccountAccessModified) chAccessModified := make(chan *pbind.AcctManagerAccountAccessModified)
chAccessRevoked := make(chan *pbind.AcctManagerAccountAccessRevoked) chAccessRevoked := make(chan *pbind.AcctManagerAccountAccessRevoked)
chStatusChanged := make(chan *pbind.AcctManagerAccountStatusChanged) chStatusChanged := make(chan *pbind.AcctManagerAccountStatusChanged)
@ -624,9 +621,25 @@ func (p *PermissionCtrl) disconnectNode(enodeId string) {
} }
func (p *PermissionCtrl) instantiateCache(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize int){
// instantiate the cache objects for permissions
types.OrgInfoMap = types.NewOrgCache(orgCacheSize)
types.OrgInfoMap.PopulateCacheFunc(p.populateOrgToCache)
types.RoleInfoMap = types.NewRoleCache(roleCacheSize)
types.RoleInfoMap.PopulateCacheFunc(p.populateRoleToCache)
types.NodeInfoMap = types.NewNodeCache(nodeCacheSize)
types.NodeInfoMap.PopulateCacheFunc(p.populateNodeCache)
types.NodeInfoMap.PopulateValidateFunc(p.populateNodeCacheAndValidate)
types.AcctInfoMap = types.NewAcctCache(accountCacheSize)
types.AcctInfoMap.PopulateCacheFunc(p.populateAccountToCache)
}
// Thus function checks if the initial network boot up status and if no // Thus function checks if the initial network boot up status and if no
// populates permissions model with details from permission-config.json // populates permissions model with details from permission-config.json
func (p *PermissionCtrl) populateInitPermissions() error { func (p *PermissionCtrl) populateInitPermissions(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize int) error {
auth := bind.NewKeyedTransactor(p.key) auth := bind.NewKeyedTransactor(p.key)
permInterfSession := &pbind.PermInterfaceSession{ permInterfSession := &pbind.PermInterfaceSession{
Contract: p.permInterf, Contract: p.permInterf,
@ -641,6 +654,8 @@ func (p *PermissionCtrl) populateInitPermissions() error {
}, },
} }
p.instantiateCache(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize)
networkInitialized, err := permInterfSession.GetNetworkBootStatus() networkInitialized, err := permInterfSession.GetNetworkBootStatus()
if err != nil { if err != nil {
// handle the scenario of no contract code. // handle the scenario of no contract code.
@ -828,9 +843,6 @@ func (p *PermissionCtrl) updateNetworkStatus(permissionsSession *pbind.PermInter
// monitors role management related events and updated cache // monitors role management related events and updated cache
func (p *PermissionCtrl) manageRolePermissions() error { func (p *PermissionCtrl) manageRolePermissions() error {
// populate function for cache population
types.RoleInfoMap.PopulateCacheFunc(p.populateRoleToCache)
chRoleCreated := make(chan *pbind.RoleManagerRoleCreated, 1) chRoleCreated := make(chan *pbind.RoleManagerRoleCreated, 1)
chRoleRevoked := make(chan *pbind.RoleManagerRoleRevoked, 1) chRoleRevoked := make(chan *pbind.RoleManagerRoleRevoked, 1)

View File

@ -9,6 +9,7 @@ import (
"log" "log"
"math/big" "math/big"
"os" "os"
"strconv"
"testing" "testing"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -41,10 +42,16 @@ const (
arbitraryOrgAdminRole = "ORG_ADMIN_ROLE" arbitraryOrgAdminRole = "ORG_ADMIN_ROLE"
arbitraryNode1 = "enode://ac6b1096ca56b9f6d004b779ae3728bf83f8e22453404cc3cef16a3d9b96608bc67c4b30db88e0a5a6c6390213f7acbe1153ff6d23ce57380104288ae19373ef@127.0.0.1:21000?discport=0&raftport=50401" arbitraryNode1 = "enode://ac6b1096ca56b9f6d004b779ae3728bf83f8e22453404cc3cef16a3d9b96608bc67c4b30db88e0a5a6c6390213f7acbe1153ff6d23ce57380104288ae19373ef@127.0.0.1:21000?discport=0&raftport=50401"
arbitraryNode2 = "enode://0ba6b9f606a43a95edc6247cdb1c1e105145817be7bcafd6b2c0ba15d58145f0dc1a194f70ba73cd6f4cdd6864edc7687f311254c7555cc32e4d45aeb1b80416@127.0.0.1:21001?discport=0&raftport=50402" arbitraryNode2 = "enode://0ba6b9f606a43a95edc6247cdb1c1e105145817be7bcafd6b2c0ba15d58145f0dc1a194f70ba73cd6f4cdd6864edc7687f311254c7555cc32e4d45aeb1b80416@127.0.0.1:21001?discport=0&raftport=50402"
arbitraryNode3 = "enode://579f786d4e2830bbcc02815a27e8a9bacccc9605df4dc6f20bcc1a6eb391e7225fff7cb83e5b4ecd1f3a94d8b733803f2f66b7e871961e7b029e22c155c3a778@127.0.0.1:21002?discport=0&raftport=50403"
arbitraryNode4 = "enode://3d9ca5956b38557aba991e31cf510d4df641dce9cc26bfeb7de082f0c07abb6ede3a58410c8f249dabeecee4ad3979929ac4c7c496ad20b8cfdd061b7401b4f5@127.0.0.1:21003?discport=0&raftport=50404"
arbitraryOrgToAdd = "ORG1" arbitraryOrgToAdd = "ORG1"
arbitrarySubOrg = "SUB1" arbitrarySubOrg = "SUB1"
arbitrartNewRole1 = "NEW_ROLE_1" arbitrartNewRole1 = "NEW_ROLE_1"
arbitrartNewRole2 = "NEW_ROLE_2" arbitrartNewRole2 = "NEW_ROLE_2"
orgCacheSize = 4
roleCacheSize = 4
nodeCacheSize = 2
accountCacheSize = 4
) )
var ErrAccountsLinked = errors.New("Accounts linked to the role. Cannot be removed") var ErrAccountsLinked = errors.New("Accounts linked to the role. Cannot be removed")
@ -201,7 +208,7 @@ func TestPermissionCtrl_PopulateInitPermissions_AfterNetworkIsInitialized(t *tes
testObject := typicalPermissionCtrl(t) testObject := typicalPermissionCtrl(t)
assert.NoError(t, testObject.AfterStart()) assert.NoError(t, testObject.AfterStart())
err := testObject.populateInitPermissions() err := testObject.populateInitPermissions(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize)
assert.NoError(t, err) assert.NoError(t, err)
@ -241,7 +248,7 @@ func typicalQuorumControlsAPI(t *testing.T) *QuorumControlsAPI {
if !assert.NoError(t, pc.AfterStart()) { if !assert.NoError(t, pc.AfterStart()) {
t.Fail() t.Fail()
} }
if !assert.NoError(t, pc.populateInitPermissions()) { if !assert.NoError(t, pc.populateInitPermissions(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize)) {
t.Fail() t.Fail()
} }
return NewQuorumControlsAPI(pc) return NewQuorumControlsAPI(pc)
@ -323,9 +330,21 @@ func TestQuorumControlsAPI_OrgAPIs(t *testing.T) {
_, err = testObject.AddSubOrg(arbitraryNetworkAdminOrg, "", "", txa) _, err = testObject.AddSubOrg(arbitraryNetworkAdminOrg, "", "", txa)
assert.Equal(t, err, errors.New("Invalid input")) assert.Equal(t, err, errors.New("Invalid input"))
_, err = testObject.GetOrgDetails(arbitraryOrgToAdd) // caching tests - cache size for org is 4. add 4 sub orgs
assert.NoError(t, err) // this will result in cache eviction
// get org details after this
for i := 0; i < orgCacheSize; i++ {
subOrgId:= "TESTSUBORG" + strconv.Itoa(i)
_, err = testObject.AddSubOrg(arbitraryNetworkAdminOrg, subOrgId, "", txa)
assert.NoError(t, err)
types.OrgInfoMap.UpsertOrg(subOrgId, arbitraryNetworkAdminOrg, arbitraryNetworkAdminOrg, big.NewInt(2), types.OrgApproved)
}
assert.Equal(t, orgCacheSize, len(types.OrgInfoMap.GetOrgList()))
orgDetails, err := testObject.GetOrgDetails(arbitraryNetworkAdminOrg)
assert.Equal(t, orgDetails.AcctList[0].AcctId, guardianAddress)
assert.Equal(t, orgDetails.RoleList[0].RoleId, arbitraryNetworkAdminRole)
} }
func TestQuorumControlsAPI_NodeAPIs(t *testing.T) { func TestQuorumControlsAPI_NodeAPIs(t *testing.T) {
@ -371,6 +390,20 @@ func TestQuorumControlsAPI_NodeAPIs(t *testing.T) {
_, err = testObject.ApproveBlackListedNodeRecovery(arbitraryNetworkAdminOrg, arbitraryNode2, txa) _, err = testObject.ApproveBlackListedNodeRecovery(arbitraryNetworkAdminOrg, arbitraryNode2, txa)
assert.NoError(t, err) assert.NoError(t, err)
types.NodeInfoMap.UpsertNode(arbitraryNetworkAdminOrg, arbitraryNode2, types.NodeApproved) types.NodeInfoMap.UpsertNode(arbitraryNetworkAdminOrg, arbitraryNode2, types.NodeApproved)
// caching tests - cache size for node is 3. add 2 nodes which will
// result in node eviction from cache. get evicted node details using api
_, err = testObject.AddNode(arbitraryNetworkAdminOrg, arbitraryNode3, txa)
assert.NoError(t, err)
types.NodeInfoMap.UpsertNode(arbitraryNetworkAdminOrg, arbitraryNode3, types.NodeApproved)
_, err = testObject.AddNode(arbitraryNetworkAdminOrg, arbitraryNode4, txa)
assert.NoError(t, err)
types.NodeInfoMap.UpsertNode(arbitraryNetworkAdminOrg, arbitraryNode4, types.NodeApproved)
assert.Equal(t, nodeCacheSize, len(types.NodeInfoMap.GetNodeList()))
nodeInfo := types.NodeInfoMap.GetNodeByUrl(arbitraryNode4)
assert.Equal(t,types.NodeApproved, nodeInfo.Status )
} }
func TestQuorumControlsAPI_RoleAndAccountsAPIs(t *testing.T) { func TestQuorumControlsAPI_RoleAndAccountsAPIs(t *testing.T) {
@ -457,6 +490,36 @@ func TestQuorumControlsAPI_RoleAndAccountsAPIs(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
types.AcctInfoMap.UpsertAccount(arbitraryNetworkAdminOrg, arbitrartNewRole2, acct, true, types.AcctActive) types.AcctInfoMap.UpsertAccount(arbitraryNetworkAdminOrg, arbitrartNewRole2, acct, true, types.AcctActive)
// check role cache. the cache size is set to 4
// insert 4 records and then retrieve the 1st role
for i := 0; i < roleCacheSize; i++ {
roleId := "TESTROLE" + strconv.Itoa(i)
_, err = testObject.AddNewRole(arbitraryNetworkAdminOrg, roleId, uint8(types.FullAccess), false, false, txa)
assert.NoError(t, err)
types.RoleInfoMap.UpsertRole(arbitraryNetworkAdminOrg, roleId, false, false, types.FullAccess, true)
}
assert.Equal(t, roleCacheSize, len(types.RoleInfoMap.GetRoleList()))
roleInfo := types.RoleInfoMap.GetRole(arbitraryNetworkAdminOrg, arbitrartNewRole1)
assert.Equal(t, roleInfo.RoleId, arbitrartNewRole1)
// check account cache
var AccountArray [4]common.Address
AccountArray[0] = common.StringToAddress("0fbdc686b912d7722dc86510934589e0aaf3b55a")
AccountArray[1] = common.StringToAddress("9186eb3d20cbd1f5f992a950d808c4495153abd5")
AccountArray[2] = common.StringToAddress("0638e1574728b6d862dd5d3a3e0942c3be47d996")
AccountArray[3] = common.StringToAddress("ae9bc6cd5145e67fbd1887a5145271fd182f0ee7")
for i := 0; i < accountCacheSize; i++ {
_, err = testObject.AddAccountToOrg(AccountArray[i], arbitraryNetworkAdminOrg, arbitrartNewRole1, txa)
assert.NoError(t, err)
types.AcctInfoMap.UpsertAccount(arbitraryNetworkAdminOrg, arbitrartNewRole1, AccountArray[i], false, types.AcctActive)
}
assert.Equal(t, accountCacheSize, len(types.AcctInfoMap.GetAcctList()))
acctInfo := types.AcctInfoMap.GetAccount(acct)
assert.True(t, acctInfo != nil, "account details nil")
} }
func getArbitraryAccount() common.Address { func getArbitraryAccount() common.Address {
@ -480,8 +543,8 @@ func typicalPermissionCtrl(t *testing.T) *PermissionCtrl {
Accounts: []common.Address{ Accounts: []common.Address{
guardianAddress, guardianAddress,
}, },
SubOrgDepth: big.NewInt(3), SubOrgDepth: big.NewInt(10),
SubOrgBreadth: big.NewInt(3), SubOrgBreadth: big.NewInt(10),
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -512,7 +575,7 @@ func TestPermissionCtrl_whenUpdateFile(t *testing.T) {
testObject := typicalPermissionCtrl(t) testObject := typicalPermissionCtrl(t)
assert.NoError(t, testObject.AfterStart()) assert.NoError(t, testObject.AfterStart())
err := testObject.populateInitPermissions() err := testObject.populateInitPermissions(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize)
assert.NoError(t, err) assert.NoError(t, err)
d, _ := ioutil.TempDir("", "qdata") d, _ := ioutil.TempDir("", "qdata")