Skip to content

Commit 695e862

Browse files
Merge pull request #16849 from enj/enj/i/new_project_admin_sar/16716
Automatic merge from submit-queue. Make admin project creation wait for SAR This change adds a SAR check to direct project creation to ensure that the designated user can get the project which was created for them. It also updates the project integration tests to be more tolerant of the project ACL being out of date. This race condition became more apparent as we moved to the generated clients since those clients were smaller and had their own rate limiters (instead of a one big client that could do everything and shared the same rate limiter). Since the new clients would perform actions at a faster pace, the race against the project ACL would occur more frequently. Signed-off-by: Monis Khan <[email protected]> Fixes #16716 /kind bug /assign @smarterclayton @simo5 @deads2k
2 parents d9ef240 + 64f17ae commit 695e862

File tree

4 files changed

+61
-31
lines changed

4 files changed

+61
-31
lines changed

pkg/oc/admin/project/new_project.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ import (
44
"errors"
55
"fmt"
66
"io"
7+
"time"
78

89
"github.com/spf13/cobra"
910

1011
kerrors "k8s.io/apimachinery/pkg/api/errors"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
errorsutil "k8s.io/apimachinery/pkg/util/errors"
14+
"k8s.io/apimachinery/pkg/util/wait"
1315
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
1416
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
1517

1618
oapi "github.com/openshift/origin/pkg/api"
19+
authorizationapi "github.com/openshift/origin/pkg/authorization/apis/authorization"
1720
authorizationtypedclient "github.com/openshift/origin/pkg/authorization/generated/internalclientset/typed/authorization/internalversion"
1821
authorizationregistryutil "github.com/openshift/origin/pkg/authorization/registry/util"
1922
"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
@@ -33,6 +36,7 @@ type NewProjectOptions struct {
3336

3437
ProjectClient projectclient.ProjectInterface
3538
RoleBindingClient authorizationtypedclient.RoleBindingsGetter
39+
SARClient authorizationtypedclient.SubjectAccessReviewInterface
3640

3741
AdminRole string
3842
AdminUser string
@@ -93,7 +97,9 @@ func (o *NewProjectOptions) complete(f *clientcmd.Factory, args []string) error
9397
if err != nil {
9498
return err
9599
}
96-
o.RoleBindingClient = authorizationClient.Authorization()
100+
authorizationInterface := authorizationClient.Authorization()
101+
o.RoleBindingClient = authorizationInterface
102+
o.SARClient = authorizationInterface.SubjectAccessReviews()
97103

98104
return nil
99105
}
@@ -133,6 +139,27 @@ func (o *NewProjectOptions) Run(useNodeSelector bool) error {
133139
if err := adduser.AddRole(); err != nil {
134140
fmt.Printf("%v could not be added to the %v role: %v\n", o.AdminUser, o.AdminRole, err)
135141
errs = append(errs, err)
142+
} else {
143+
if err := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
144+
resp, err := o.SARClient.Create(&authorizationapi.SubjectAccessReview{
145+
Action: authorizationapi.Action{
146+
Namespace: o.ProjectName,
147+
Verb: "get",
148+
Resource: "projects",
149+
},
150+
User: o.AdminUser,
151+
})
152+
if err != nil {
153+
return false, err
154+
}
155+
if !resp.Allowed {
156+
return false, nil
157+
}
158+
return true, nil
159+
}); err != nil {
160+
fmt.Printf("%s is not able to get project %s with the %s role: %v\n", o.AdminUser, o.ProjectName, o.AdminRole, err)
161+
errs = append(errs, err)
162+
}
136163
}
137164
}
138165

test/integration/login_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ func TestLogin(t *testing.T) {
4747
if loginOptions.Username != username {
4848
t.Fatalf("Unexpected user after authentication: %#v", loginOptions)
4949
}
50+
authorizationInterface := authorizationclient.NewForConfigOrDie(clusterAdminClientConfig).Authorization()
5051

5152
newProjectOptions := &newproject.NewProjectOptions{
5253
ProjectClient: projectclient.NewForConfigOrDie(clusterAdminClientConfig).Project(),
53-
RoleBindingClient: authorizationclient.NewForConfigOrDie(clusterAdminClientConfig),
54+
RoleBindingClient: authorizationInterface,
55+
SARClient: authorizationInterface.SubjectAccessReviews(),
5456
ProjectName: project,
5557
AdminRole: bootstrappolicy.AdminRoleName,
5658
AdminUser: username,

test/integration/project_test.go

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/openshift/origin/pkg/oc/admin/policy"
2727
projectapi "github.com/openshift/origin/pkg/project/apis/project"
2828
projectclient "github.com/openshift/origin/pkg/project/generated/internalclientset"
29+
projectinternalversion "github.com/openshift/origin/pkg/project/generated/internalclientset/typed/project/internalversion"
2930
testutil "github.com/openshift/origin/test/util"
3031
testserver "github.com/openshift/origin/test/util/server"
3132
)
@@ -467,25 +468,15 @@ func TestScopedProjectAccess(t *testing.T) {
467468
}
468469
waitForOnlyAdd("four", allWatch, t)
469470

470-
oneTwoProjects, err := oneTwoBobClient.Projects().List(metav1.ListOptions{})
471-
if err != nil {
472-
t.Fatalf("unexpected error: %v", err)
473-
}
474-
if err := hasExactlyTheseProjects(oneTwoProjects, sets.NewString("one", "two")); err != nil {
471+
if err := hasExactlyTheseProjects(oneTwoBobClient.Projects(), sets.NewString("one", "two")); err != nil {
475472
t.Error(err)
476473
}
477-
twoThreeProjects, err := twoThreeBobClient.Projects().List(metav1.ListOptions{})
478-
if err != nil {
479-
t.Fatalf("unexpected error: %v", err)
480-
}
481-
if err := hasExactlyTheseProjects(twoThreeProjects, sets.NewString("two", "three")); err != nil {
474+
475+
if err := hasExactlyTheseProjects(twoThreeBobClient.Projects(), sets.NewString("two", "three")); err != nil {
482476
t.Error(err)
483477
}
484-
allProjects, err := allBobClient.Projects().List(metav1.ListOptions{})
485-
if err != nil {
486-
t.Fatalf("unexpected error: %v", err)
487-
}
488-
if err := hasExactlyTheseProjects(allProjects, sets.NewString("one", "two", "three", "four")); err != nil {
478+
479+
if err := hasExactlyTheseProjects(allBobClient.Projects(), sets.NewString("one", "two", "three", "four")); err != nil {
489480
t.Error(err)
490481
}
491482

@@ -589,15 +580,11 @@ func TestInvalidRoleRefs(t *testing.T) {
589580
}
590581

591582
// Make sure bob still sees his project (and only his project)
592-
if projects, err := projectclient.NewForConfigOrDie(bobConfig).Projects().List(metav1.ListOptions{}); err != nil {
593-
t.Fatalf("unexpected error: %v", err)
594-
} else if hasErr := hasExactlyTheseProjects(projects, sets.NewString("foo")); hasErr != nil {
583+
if hasErr := hasExactlyTheseProjects(projectclient.NewForConfigOrDie(bobConfig).Projects(), sets.NewString("foo")); hasErr != nil {
595584
t.Error(hasErr)
596585
}
597586
// Make sure alice still sees her project (and only her project)
598-
if projects, err := projectclient.NewForConfigOrDie(aliceConfig).Projects().List(metav1.ListOptions{}); err != nil {
599-
t.Fatalf("unexpected error: %v", err)
600-
} else if hasErr := hasExactlyTheseProjects(projects, sets.NewString("bar")); hasErr != nil {
587+
if hasErr := hasExactlyTheseProjects(projectclient.NewForConfigOrDie(aliceConfig).Projects(), sets.NewString("bar")); hasErr != nil {
601588
t.Error(hasErr)
602589
}
603590
// Make sure cluster admin still sees all projects
@@ -614,14 +601,26 @@ func TestInvalidRoleRefs(t *testing.T) {
614601
}
615602
}
616603

617-
func hasExactlyTheseProjects(list *projectapi.ProjectList, projects sets.String) error {
618-
if len(list.Items) != len(projects) {
619-
return fmt.Errorf("expected %v, got %v", projects, list.Items)
620-
}
621-
for _, project := range list.Items {
622-
if !projects.Has(project.Name) {
623-
return fmt.Errorf("expected %v, got %v", projects, list.Items)
604+
func hasExactlyTheseProjects(lister projectinternalversion.ProjectResourceInterface, projects sets.String) error {
605+
var lastErr error
606+
if err := wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (bool, error) {
607+
list, err := lister.List(metav1.ListOptions{})
608+
if err != nil {
609+
return false, err
624610
}
611+
if len(list.Items) != len(projects) {
612+
lastErr = fmt.Errorf("expected %v, got %v", projects.List(), list.Items)
613+
return false, nil
614+
}
615+
for _, project := range list.Items {
616+
if !projects.Has(project.Name) {
617+
lastErr = fmt.Errorf("expected %v, got %v", projects.List(), list.Items)
618+
return false, nil
619+
}
620+
}
621+
return true, nil
622+
}); err != nil {
623+
return fmt.Errorf("hasExactlyTheseProjects failed with %v and %v", err, lastErr)
625624
}
626625
return nil
627626
}

test/util/server/server.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,10 +617,12 @@ func CreateNewProject(clientConfig *restclient.Config, projectName, adminUser st
617617
if err != nil {
618618
return nil, nil, err
619619
}
620+
authorizationInterface := authorizationClient.Authorization()
620621

621622
newProjectOptions := &newproject.NewProjectOptions{
622623
ProjectClient: projectClient,
623-
RoleBindingClient: authorizationClient.Authorization(),
624+
RoleBindingClient: authorizationInterface,
625+
SARClient: authorizationInterface.SubjectAccessReviews(),
624626
ProjectName: projectName,
625627
AdminRole: bootstrappolicy.AdminRoleName,
626628
AdminUser: adminUser,

0 commit comments

Comments
 (0)