This is an automated email from the ASF dual-hosted git repository.
pbacsko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/yunikorn-core.git
The following commit(s) were added to refs/heads/master by this push:
new 6c8fd60b [YUNIKORN-2697] Improve usergroup funtion's test coverage
(#901)
6c8fd60b is described below
commit 6c8fd60b82fd0c1973a1c1aa9717fb6bf4440c30
Author: SP12893678 <[email protected]>
AuthorDate: Mon Jul 15 00:49:56 2024 +0200
[YUNIKORN-2697] Improve usergroup funtion's test coverage (#901)
Closes: #901
Signed-off-by: Peter Bacsko <[email protected]>
---
pkg/common/security/usergroup_test.go | 164 ++++++++++++++++++++-----
pkg/common/security/usergroup_test_resolver.go | 25 ++++
2 files changed, 155 insertions(+), 34 deletions(-)
diff --git a/pkg/common/security/usergroup_test.go
b/pkg/common/security/usergroup_test.go
index c1c9873e..0540fe04 100644
--- a/pkg/common/security/usergroup_test.go
+++ b/pkg/common/security/usergroup_test.go
@@ -19,32 +19,76 @@
package security
import (
+ "fmt"
+ "reflect"
"strings"
"testing"
+ "time"
"gotest.tools/v3/assert"
+ "github.com/apache/yunikorn-core/pkg/common"
"github.com/apache/yunikorn-scheduler-interface/lib/go/si"
)
+func (c *UserGroupCache) getUGsize() int {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return len(c.ugs)
+}
+
+func (c *UserGroupCache) getUGGroupSize(user string) int {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ ug := c.ugs[user]
+ return len(ug.Groups)
+}
+
+func (c *UserGroupCache) getUGmap() map[string]*UserGroup {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.ugs
+}
+
func TestGetUserGroupCache(t *testing.T) {
// get the cache with the test resolver set
testCache := GetUserGroupCache("test")
- if testCache == nil {
- t.Fatal("Cache create failed")
- }
- if len(testCache.ugs) != 0 {
- t.Errorf("Cache not empty: %v", testCache.ugs)
- }
+ assert.Assert(t, testCache != nil, "Cache create failed")
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
+
+ testCache.Stop()
+ assert.Assert(t, instance == nil, "instance should be nil")
+ assert.Assert(t, stopped.Load())
+
+ // get the cache with the os resolver set
+ testCache = GetUserGroupCache("os")
+ assert.Assert(t, testCache != nil, "Cache create failed")
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
+
+ testCache.Stop()
+ assert.Assert(t, instance == nil, "instance should be nil")
+ assert.Assert(t, stopped.Load())
+
+ // get the cache with the default resolver set
+ testCache = GetUserGroupCache("unknown")
+ assert.Assert(t, testCache != nil, "Cache create failed")
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
+
+ testCache.Stop()
+ assert.Assert(t, instance == nil, "instance should be nil")
+ assert.Assert(t, stopped.Load())
+
+ // test for re stop again
+ testCache.Stop()
+ assert.Assert(t, instance == nil, "instance should be nil")
+ assert.Assert(t, stopped.Load())
}
func TestGetUserGroup(t *testing.T) {
testCache := GetUserGroupCache("test")
testCache.resetCache()
// test cache should be empty now
- if len(testCache.ugs) != 0 {
- t.Fatalf("Cache not empty: %v", testCache.ugs)
- }
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
ug, err := testCache.GetUserGroup("testuser1")
assert.NilError(t, err, "Lookup should not have failed: testuser1")
@@ -58,12 +102,15 @@ func TestGetUserGroup(t *testing.T) {
if ug.User != "testuser1" || len(ug.Groups) != 2 || ug.resolved == 0 ||
ug.failed {
t.Errorf("User 'testuser1' not resolved correctly: %v", ug)
}
+ testCache.lock.Lock()
cachedUG := testCache.ugs["testuser1"]
if ug.resolved != cachedUG.resolved {
t.Errorf("User 'testuser1' not cached correctly resolution time
differs: %d got %d", ug.resolved, cachedUG.resolved)
}
// click over the clock: if we do not get the cached version the new
time will differ from the cache update
cachedUG.resolved -= 5
+ testCache.lock.Unlock()
+
ug, err = testCache.GetUserGroup("testuser1")
if err != nil || ug.resolved != cachedUG.resolved {
t.Errorf("User 'testuser1' not returned from Cache, resolution
time differs: %d got %d (err = %v)", ug.resolved, cachedUG.resolved, err)
@@ -74,17 +121,14 @@ func TestBrokenUserGroup(t *testing.T) {
testCache := GetUserGroupCache("test")
testCache.resetCache()
// test cache should be empty now
- if len(testCache.ugs) != 0 {
- t.Fatalf("Cache not empty: %v", testCache.ugs)
- }
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
ug, err := testCache.GetUserGroup("testuser2")
if err != nil {
t.Error("Lookup should not have failed: testuser2")
}
- if len(testCache.ugs) != 1 {
- t.Errorf("Cache not updated should have 1 entry %d",
len(testCache.ugs))
- }
+
+ assert.Equal(t, 1, testCache.getUGsize(), "Cache not updated should
have 1 entry %d", testCache.getUGmap())
// check returned info: 3 groups etc
if ug.User != "testuser2" || len(ug.Groups) != 3 || ug.resolved == 0 ||
ug.failed {
t.Errorf("User 'testuser2' not resolved correctly: %v", ug)
@@ -98,22 +142,30 @@ func TestBrokenUserGroup(t *testing.T) {
if err != nil {
t.Error("Lookup should not have failed: testuser3")
}
- if len(testCache.ugs) != 2 {
- t.Errorf("Cache not updated should have 2 entries %d",
len(testCache.ugs))
- }
- // check returned info: 4 groups, primary group is duplicate
- if len(ug.Groups) != 4 {
- t.Errorf("User 'testuser3' not resolved correctly: duplicate
primary group not filtered %v", ug)
- }
+
+ assert.Equal(t, 2, testCache.getUGsize(), "Cache not updated should
have 2 entries %d", len(testCache.ugs))
+ assert.Equal(t, 4, testCache.getUGGroupSize("testuser3"), "User
'testuser3' not resolved correctly: duplicate primary group not filtered %v",
ug)
+
+ ug, err = testCache.GetUserGroup("unknown")
+ assert.ErrorContains(t, err, "lookup failed for user: unknown")
+
+ ug, err = testCache.GetUserGroup("testuser4")
+ assert.NilError(t, err)
+
+ ug, err = testCache.GetUserGroup("testuser5")
+ assert.ErrorContains(t, err, "lookup failed for user: testuser5")
+
+ ug, err = testCache.GetUserGroup("invalid-gid-user")
+ assert.ErrorContains(t, err, "lookup failed for user: invalid-gid-user")
+ exceptedGroup := []string{"1_001"}
+ assert.Assert(t, reflect.DeepEqual(ug.Groups, exceptedGroup),
fmt.Errorf("group should be: %v, but got: %v", exceptedGroup, ug.Groups))
}
func TestGetUserGroupFail(t *testing.T) {
testCache := GetUserGroupCache("test")
testCache.resetCache()
// test cache should be empty now
- if len(testCache.ugs) != 0 {
- t.Fatalf("Cache not empty: %v", testCache.ugs)
- }
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
// resolve an empty user
ug, err := testCache.GetUserGroup("")
@@ -134,6 +186,7 @@ func TestGetUserGroupFail(t *testing.T) {
if ug.User != "unknown" || len(ug.Groups) != 0 || !ug.failed {
t.Errorf("UserGroup is not empty: %v", ug)
}
+
ug, err = testCache.GetUserGroup("unknown")
if err == nil {
t.Error("Lookup should have failed: unknown user")
@@ -148,9 +201,7 @@ func TestCacheCleanUp(t *testing.T) {
testCache := GetUserGroupCache("test")
testCache.resetCache()
// test cache should be empty now
- if len(testCache.ugs) != 0 {
- t.Fatalf("Cache not empty: %v", testCache.ugs)
- }
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
// resolve an existing user
_, err := testCache.GetUserGroup("testuser1")
@@ -162,38 +213,75 @@ func TestCacheCleanUp(t *testing.T) {
t.Error("Lookup should not have failed: testuser2 user")
}
+ testCache.lock.Lock()
ug := testCache.ugs["testuser1"]
if ug.failed {
t.Error("User 'testuser1' not resolved as a success")
}
// expire the successful lookup
ug.resolved -= 2 * poscache
+ testCache.lock.Unlock()
// resolve a non existing user
_, err = testCache.GetUserGroup("unknown")
if err == nil {
t.Error("Lookup should have failed: unknown user")
}
+ testCache.lock.Lock()
ug = testCache.ugs["unknown"]
if !ug.failed {
t.Error("User 'unknown' not resolved as a failure")
}
// expire the failed lookup
ug.resolved -= 2 * negcache
+ testCache.lock.Unlock()
testCache.cleanUpCache()
- if len(testCache.ugs) != 1 {
- t.Errorf("Cache not cleaned up : %v", testCache.ugs)
- }
+ assert.Equal(t, 1, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
+}
+
+func TestIntervalCacheCleanUp(t *testing.T) {
+ testCache := GetUserGroupCache("test")
+ testCache.resetCache()
+ // test cache should be empty now
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
+
+ // resolve an existing user
+ user1ug, err := testCache.GetUserGroup("testuser1")
+ assert.NilError(t, err, "Lookup should not have failed: testuser1 user")
+ assert.Assert(t, !user1ug.failed, "User 'testuser1' not resolved as a
success")
+
+ _, err = testCache.GetUserGroup("testuser2")
+ assert.NilError(t, err, "Lookup should not have failed: testuser1 user")
+
+ // expire the successful lookup
+ testCache.lock.Lock()
+ ug := testCache.ugs["testuser1"]
+ ug.resolved -= 2 * poscache
+
+ testCache.lock.Unlock()
+ // resolve a non existing user
+ _, err = testCache.GetUserGroup("unknown")
+ assert.Assert(t, err != nil, "Lookup should have failed: unknown user")
+ testCache.lock.Lock()
+ ug = testCache.ugs["unknown"]
+ assert.Assert(t, ug.failed, "User 'unknown' not resolved as a failure")
+
+ // expire the failed lookup
+ ug.resolved -= 2 * negcache
+ testCache.lock.Unlock()
+
+ // sleep to wait for interval, it will trigger cleanUpCache
+ time.Sleep(testCache.interval + time.Second)
+ assert.Equal(t, 1, testCache.getUGsize(), "Cache not cleaned up : %v",
testCache.getUGmap())
}
func TestConvertUGI(t *testing.T) {
testCache := GetUserGroupCache("test")
testCache.resetCache()
// test cache should be empty now
- if len(testCache.ugs) != 0 {
- t.Fatalf("Cache not empty: %v", testCache.ugs)
- }
+ assert.Equal(t, 0, testCache.getUGsize(), "Cache is not empty: %v",
testCache.getUGmap())
+
ugi := &si.UserGroupInformation{
User: "",
Groups: nil,
@@ -251,4 +339,12 @@ func TestConvertUGI(t *testing.T) {
if err == nil {
t.Errorf("invalid username, convert should have failed: %v",
err)
}
+
+ // try unknown user with empty group when forced
+ ugi.User = "unknown"
+ ugi.Groups = []string{}
+ ug, err = testCache.ConvertUGI(ugi, true)
+ exceptedGroup := []string{common.AnonymousGroup}
+ assert.Assert(t, reflect.DeepEqual(ug.Groups, exceptedGroup), "group
should be: %v, but got: %v", exceptedGroup, ug.Groups)
+ assert.NilError(t, err, "unknown user, no groups, convert should not
have failed")
}
diff --git a/pkg/common/security/usergroup_test_resolver.go
b/pkg/common/security/usergroup_test_resolver.go
index c22ce570..b22b60b2 100644
--- a/pkg/common/security/usergroup_test_resolver.go
+++ b/pkg/common/security/usergroup_test_resolver.go
@@ -63,6 +63,27 @@ func lookup(userName string) (*user.User, error) {
Username: "testuser3",
}, nil
}
+ if userName == "testuser4" {
+ return &user.User{
+ Uid: "901",
+ Gid: "901",
+ Username: "testuser4",
+ }, nil
+ }
+ if userName == "testuser5" {
+ return &user.User{
+ Uid: "1001",
+ Gid: "1001",
+ Username: "testuser5",
+ }, nil
+ }
+ if userName == "invalid-gid-user" {
+ return &user.User{
+ Uid: "1001",
+ Gid: "1_001",
+ Username: "invalid-gid-user",
+ }, nil
+ }
// all other users fail
return nil, fmt.Errorf("lookup failed for user: %s", userName)
}
@@ -95,5 +116,9 @@ func groupIds(osUser *user.User) ([]string, error) {
if osUser.Username == "testuser3" {
return []string{"1002", "1001", "1003", "1004"}, nil
}
+
+ if osUser.Username == "testuser4" {
+ return []string{"901", "902"}, nil
+ }
return nil, fmt.Errorf("lookup failed for user: %s", osUser.Username)
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]