Skip to content

ProjectRequestLimit plugin: ignore projects in terminating state #8400

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion pkg/project/admission/requestlimit/admission.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import (
projectcache "github.com/openshift/origin/pkg/project/cache"
)

// allowedTerminatingProjects is the number of projects that are owned by a user, are in terminating state,
// and do not count towards the user's limit.
const allowedTerminatingProjects = 2

func init() {
admission.RegisterPlugin("ProjectRequestLimit", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
pluginConfig, err := readConfig(config)
Expand Down Expand Up @@ -116,7 +120,24 @@ func (o *projectRequestLimit) projectCountByRequester(userName string) (int, err
if err != nil {
return 0, err
}
return len(namespaces), nil

terminatingCount := 0
for _, obj := range namespaces {
ns, ok := obj.(*kapi.Namespace)
if !ok {
return 0, fmt.Errorf("object in cache is not a namespace: %#v", obj)
}
if ns.Status.Phase == kapi.NamespaceTerminating {
terminatingCount++
}
}
count := len(namespaces)
if terminatingCount > allowedTerminatingProjects {
count -= allowedTerminatingProjects
} else {
count -= terminatingCount
}
return count, nil
}

func (o *projectRequestLimit) SetOpenshiftClient(client client.Interface) {
Expand Down
67 changes: 59 additions & 8 deletions pkg/project/admission/requestlimit/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,45 @@ func TestMaxProjectByRequester(t *testing.T) {
}
}

func TestProjectCountByRequester(t *testing.T) {
pCache := fakeProjectCache(map[string]projectCount{
"user1": {1, 5}, // total 6, expect 4
"user2": {5, 1}, // total 6, expect 5
"user3": {1, 0}, // total 1, expect 1
})
reqLimit := &projectRequestLimit{
cache: pCache,
}
tests := []struct {
user string
expect int
}{
{
user: "user1",
expect: 4,
},
{
user: "user2",
expect: 5,
},
{
user: "user3",
expect: 1,
},
}

for _, test := range tests {
actual, err := reqLimit.projectCountByRequester(test.user)
if err != nil {
t.Errorf("unexpected: %v", err)
}
if actual != test.expect {
t.Errorf("user %s got %d, expected %d", test.user, actual, test.expect)
}
}

}

func TestAdmit(t *testing.T) {
tests := []struct {
config *requestlimitapi.ProjectRequestLimitConfig
Expand Down Expand Up @@ -219,10 +258,11 @@ func TestAdmit(t *testing.T) {
}

for _, tc := range tests {
pCache := fakeProjectCache(map[string]int{
"user2": 2,
"user3": 5,
"user4": 1,
pCache := fakeProjectCache(map[string]projectCount{
"user1": {0, 1},
"user2": {2, 2},
"user3": {5, 3},
"user4": {1, 0},
})
client := &testclient.Fake{}
client.AddReactor("get", "users", userFn(map[string]labels.Set{
Expand Down Expand Up @@ -296,12 +336,15 @@ func configEquals(a, b *requestlimitapi.ProjectRequestLimitConfig) bool {
return true
}

func fakeNs(name string) *kapi.Namespace {
func fakeNs(name string, terminating bool) *kapi.Namespace {
ns := &kapi.Namespace{}
ns.Name = kapi.SimpleNameGenerator.GenerateName("testns")
ns.Annotations = map[string]string{
"openshift.io/requester": name,
}
if terminating {
ns.Status.Phase = kapi.NamespaceTerminating
}
return ns
}

Expand All @@ -312,12 +355,20 @@ func fakeUser(name string, labels map[string]string) *userapi.User {
return user
}

func fakeProjectCache(requesters map[string]int) *projectcache.ProjectCache {
type projectCount struct {
active int
terminating int
}

func fakeProjectCache(requesters map[string]projectCount) *projectcache.ProjectCache {
kclient := &ktestclient.Fake{}
pCache := projectcache.NewFake(kclient.Namespaces(), projectcache.NewCacheStore(cache.MetaNamespaceKeyFunc), "")
for requester, count := range requesters {
for i := 0; i < count; i++ {
pCache.Store.Add(fakeNs(requester))
for i := 0; i < count.active; i++ {
pCache.Store.Add(fakeNs(requester, false))
}
for i := 0; i < count.terminating; i++ {
pCache.Store.Add(fakeNs(requester, true))
}
}
return pCache
Expand Down