-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Add the --rolebinding-name option to rolebinding commands #14868
Add the --rolebinding-name option to rolebinding commands #14868
Conversation
test/cmd/admin.sh
Outdated
@@ -155,6 +155,30 @@ os::cmd::expect_success 'oadm policy add-cluster-role-to-group cluster-admin sys | |||
os::cmd::expect_success 'oadm policy remove-cluster-role-from-group cluster-admin system:unauthenticated' | |||
os::cmd::expect_success 'oadm policy add-cluster-role-to-user cluster-admin system:no-user' | |||
os::cmd::expect_success 'oadm policy remove-cluster-role-from-user cluster-admin system:no-user' | |||
# test the rolebinding and clusterrolebinding name flag |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please break this out into it's own file under test/cmd
, We don't need this one to grow any more.
test/cmd/admin.sh
Outdated
os::cmd::expect_success 'oadm policy add-role-to-user admin -z fake-sa1 -N rb1' | ||
os::cmd::expect_success 'oadm policy add-role-to-user admin -z fake-sa2 -N rb2' | ||
os::cmd::expect_success 'oadm policy add-role-to-user admin -z fake-sa3 -N rb2' | ||
os::cmd::expect_success_and_text 'oc get rolebinding/rb1' 'rb1 /admin fake-sa1' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When doing action-state checks with os::cmd
always prefer to use jsonpath
to explicitly query the object -- so here, I would expect:
os::cmd::expect_success_and_text "oc get rolebinding rb1 -o jsonpath='{ .roleref.name }'" "admin"
and the same for the subject
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for the jsonpath output to display those three fields from one command? For example, making the output display as 'rb1 /admin fake-sa1'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, when the paths are comma-delimited. See the doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, though I will turn these into unit tests as you suggested.
test/cmd/admin.sh
Outdated
os::cmd::expect_success 'oadm policy add-role-to-user admin -z fake-sa3 -N rb2' | ||
os::cmd::expect_success_and_text 'oc get rolebinding/rb1' 'rb1 /admin fake-sa1' | ||
os::cmd::expect_success_and_text 'oc get rolebinding/rb2' 'rb2 /admin fake-sa2, fake-sa3' | ||
os::cmd::expect_success 'oadm policy remove-role-from-user admin -z fake-sa3 -N rb2' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That being said, these look more like unit-level tests rather than cmd integration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifically -- a lot of the create and check flows seem superfluous -- we should be able to check that the struct of Options
would lead to the correct objects being passed to the client in a unit test. Writing the test like this outside only also checks that the client correctly updates the server, which we don't need to be doing here.
@openshift/cli-review |
pkg/cmd/admin/policy/modify_roles.go
Outdated
@@ -371,9 +381,16 @@ subjectCheck: | |||
} | |||
|
|||
if isUpdate { | |||
if o.RoleBindingName != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: len(o.RoleBindingName) > 0
when checking for a non-empty empty string
pkg/cmd/admin/policy/policy.go
Outdated
@@ -115,7 +115,7 @@ func getUniqueName(basename string, existingNames *sets.String) string { | |||
|
|||
// RoleBindingAccessor is used by role modification commands to access and modify roles | |||
type RoleBindingAccessor interface { | |||
GetExistingRoleBindingsForRole(roleNamespace, role string) ([]*authorizationapi.RoleBinding, error) | |||
GetExistingRoleBindingsForRole(roleNamespace, role, rb string) ([]*authorizationapi.RoleBinding, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might benefit from introducing a new method instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new method would replace both uses of GetExistingRoleBindingsForRole(), so maybe it should be renamed. Eg., GetRoleBindingByRoleNames().
pkg/cmd/admin/policy/policy.go
Outdated
ret := make([]*authorizationapi.RoleBinding, 0) | ||
|
||
for _, currBinding := range bindings { | ||
if currBinding.RoleRef.Name == role && (rb == "" || rb == currBinding.Name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: len(rb) == 0
pkg/cmd/admin/policy/modify_roles.go
Outdated
@@ -71,6 +72,7 @@ func NewCmdAddRoleToGroup(name, fullName string, f *clientcmd.Factory, out io.Wr | |||
}, | |||
} | |||
|
|||
cmd.Flags().StringVarP(&options.RoleBindingName, "rolebinding-name", "N", "", "name of the rolebinding to modify or create") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name of the rolebinding to modify or create. If left empty, overrides the first rolebinding found for the given role
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/overrides/appends to/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@openshift/cli-review is it normal to have capital parameters like -N
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
avoid claiming new single-letter flags, per https://github.com/kubernetes/community/blob/master/contributors/devel/kubectl-conventions.md#flag-conventions
pkg/cmd/admin/policy/modify_roles.go
Outdated
@@ -388,7 +405,7 @@ subjectCheck: | |||
} | |||
|
|||
func (o *RoleModificationOptions) RemoveRole() error { | |||
roleBindings, err := o.RoleBindingAccessor.GetExistingRoleBindingsForRole(o.RoleNamespace, o.RoleName) | |||
roleBindings, err := o.RoleBindingAccessor.GetExistingRoleBindingsForRole(o.RoleNamespace, o.RoleName, o.RoleBindingName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifying RoleBindingName
here seems strange to me. If you are removing a role, you probably want to remove it from all bindings. Otherwise you risk the user continuing to have the permission you just tried to remove.
pkg/cmd/admin/policy/modify_roles.go
Outdated
@@ -231,6 +238,7 @@ func NewCmdRemoveClusterRoleFromGroup(name, fullName string, f *clientcmd.Factor | |||
}, | |||
} | |||
|
|||
cmd.Flags().StringVarP(&options.RoleBindingName, "rolebinding-name", "N", "", "name of the rolebinding to modify or create") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove-cluster-role-from-group
doesn't create.
|
pkg/cmd/admin/policy/policy.go
Outdated
@@ -115,7 +115,7 @@ func getUniqueName(basename string, existingNames *sets.String) string { | |||
|
|||
// RoleBindingAccessor is used by role modification commands to access and modify roles | |||
type RoleBindingAccessor interface { | |||
GetExistingRoleBindingsForRole(roleNamespace, role string) ([]*authorizationapi.RoleBinding, error) | |||
GetExistingRoleBindingsForRole(roleNamespace, role, rb string) ([]*authorizationapi.RoleBinding, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure this parameter makes sense for this method -- also, s/rb/roleBindingName/
pkg/cmd/admin/policy/policy.go
Outdated
@@ -131,21 +131,13 @@ func NewLocalRoleBindingAccessor(bindingNamespace string, client client.Interfac | |||
return LocalRoleBindingAccessor{bindingNamespace, client} | |||
} | |||
|
|||
func (a LocalRoleBindingAccessor) GetExistingRoleBindingsForRole(roleNamespace, role string) ([]*authorizationapi.RoleBinding, error) { | |||
func (a LocalRoleBindingAccessor) GetExistingRoleBindingsForRole(roleNamespace, role, rb string) ([]*authorizationapi.RoleBinding, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Callers upstream should just filter by names themselves
Changed to filtering by name in AddRole(), leaving GetExistingRoleBindingsForRole() as-is. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pkg/cmd/admin/policy/modify_roles.go
Outdated
@@ -371,9 +390,16 @@ subjectCheck: | |||
} | |||
|
|||
if isUpdate { | |||
if len(o.RoleBindingName) > 0 { | |||
roleBinding.Name = o.RoleBindingName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setting the name here is wrong... it's either a no-op or it will fail trying to mutate a name
pkg/cmd/admin/policy/modify_roles.go
Outdated
@@ -336,6 +342,19 @@ func (o *RoleModificationOptions) AddRole() error { | |||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you only need to look up existing rolebindings if they didn't provide a rolebinding name
pkg/cmd/admin/policy/modify_roles.go
Outdated
@@ -336,6 +342,19 @@ func (o *RoleModificationOptions) AddRole() error { | |||
if err != nil { | |||
return err | |||
} | |||
|
|||
if len(o.RoleBindingName) > 0 { | |||
filteredRoleBindings := []*authorizationapi.RoleBinding{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filtering the list to a single item is weird... it also means if the named rolebinding already exists but points to a different roleref, it'll get missed here... that should be an error
pkg/cmd/admin/policy/modify_roles.go
Outdated
roleBindings = filteredRoleBindings | ||
} | ||
} | ||
|
||
roleBindingNames, err := o.RoleBindingAccessor.GetExistingRoleBindingNames() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you only need to do this if they didn't provide a name
pkg/cmd/admin/policy/modify_roles.go
Outdated
err = o.RoleBindingAccessor.UpdateRoleBinding(roleBinding) | ||
} else { | ||
roleBinding.Name = getUniqueName(o.RoleName, roleBindingNames) | ||
if len(o.RoleBindingName) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see all this logic hoisted up to the top
if name provided {
look up named binding
if exists and points to other roleref, error
if exists, set isUpdate=true
if doesn't exist, populate object with specified name, set isUpdate=false
} else {
get bindings to role
if exists, pick first to modify, set isUpdate=true
if doesn't exist, get existing names and generate unique name, set isUpdate=false
}
everything from that point on can deal with the rolebinding the same way in either case
@liggitt thanks for the review, I've adjusted the AddRole() logic as you suggested, and added the accessor method GetRoleBinding() in order to look for a rolebinding directly by name. |
pkg/cmd/admin/policy/modify_roles.go
Outdated
isUpdate := false | ||
if len(o.RoleBindingName) > 0 { | ||
// Look for an existing rolebinding by name. | ||
roleBinding, err = o.RoleBindingAccessor.GetRoleBinding(o.RoleNamespace, o.RoleBindingName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you get an error other than NotFound
, return early
pkg/cmd/admin/policy/modify_roles.go
Outdated
// Look for an existing rolebinding by name. | ||
roleBinding, err = o.RoleBindingAccessor.GetRoleBinding(o.RoleNamespace, o.RoleBindingName) | ||
if roleBinding != nil { | ||
if roleBinding.RoleRef.Name != o.RoleName { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or if the roleRef namespace is different
pkg/cmd/admin/policy/policy.go
Outdated
@@ -165,6 +166,21 @@ func (a LocalRoleBindingAccessor) GetExistingRoleBindingNames() (*sets.String, e | |||
return ret, nil | |||
} | |||
|
|||
func (a LocalRoleBindingAccessor) GetRoleBinding(roleNamespace, roleBindingName string) (*authorizationapi.RoleBinding, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace, name string
pkg/cmd/admin/policy/policy.go
Outdated
@@ -165,6 +166,21 @@ func (a LocalRoleBindingAccessor) GetExistingRoleBindingNames() (*sets.String, e | |||
return ret, nil | |||
} | |||
|
|||
func (a LocalRoleBindingAccessor) GetRoleBinding(roleNamespace, roleBindingName string) (*authorizationapi.RoleBinding, error) { | |||
roleBindings, err := a.Client.PolicyBindings(a.BindingNamespace).Get(authorizationapi.GetPolicyBindingName(roleNamespace), metav1.GetOptions{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this not just return a.Client.RoleBindings(namespace).Get(name, metav1.GetOptions{})
?
pkg/cmd/admin/policy/policy.go
Outdated
@@ -222,6 +238,22 @@ func (a ClusterRoleBindingAccessor) GetExistingRoleBindingNames() (*sets.String, | |||
return ret, nil | |||
} | |||
|
|||
func (a ClusterRoleBindingAccessor) GetRoleBinding(roleNamespace, roleBindingName string) (*authorizationapi.RoleBinding, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you only need a name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you have to conform to the interface, but namespace should always be empty
pkg/cmd/admin/policy/policy.go
Outdated
@@ -222,6 +238,22 @@ func (a ClusterRoleBindingAccessor) GetExistingRoleBindingNames() (*sets.String, | |||
return ret, nil | |||
} | |||
|
|||
func (a ClusterRoleBindingAccessor) GetRoleBinding(roleNamespace, roleBindingName string) (*authorizationapi.RoleBinding, error) { | |||
uncast, err := a.Client.ClusterPolicyBindings().Get(authorizationapi.GetPolicyBindingName(roleNamespace), metav1.GetOptions{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return a.Client.ClusterRoleBindings().Get(name, metav1.GetOptions{})
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you have to convert the result in non-error cases
/test end_to_end |
After Mo's in-person feedback, I've refactored the revised AddRole logic and simplified the tests. Also rebased. |
/test extended_conformance_install_update |
Flake #11114 |
/retest |
5a26326
to
79714d0
Compare
Add the --rolebinding-name option to the rolebinding and clusterrolebinding add commands for specifying the name of the rolebinding to modify. Signed-off-by: Matt Rogers <[email protected]>
79714d0
to
a3ab73a
Compare
/lgtm |
@fabianofranz can you approve this? |
flake #13966 |
/retest |
/test extended_conformance_install_update /lgtm |
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: enj, juanvallejo, mrogers950 Assign the PR to them by writing The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these OWNERS Files:
You can indicate your approval by writing |
Approving based on feedback from @juanvallejo |
/retest |
/retest Please review the full test history for this PR and help us cut down flakes. |
1 similar comment
/retest Please review the full test history for this PR and help us cut down flakes. |
Automatic merge from submit-queue |
Add a
--rolebinding-name
option to the rolebinding and clusterrolebinding commands for specifying the name of the rolebinding to modify.Fixes #13035