Skip to content
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

Merged
merged 2 commits into from
Sep 1, 2017

Conversation

mrogers950
Copy link
Contributor

@mrogers950 mrogers950 commented Jun 23, 2017

Add a --rolebinding-name option to the rolebinding and clusterrolebinding commands for specifying the name of the rolebinding to modify.

Fixes #13035

@mrogers950 mrogers950 requested review from enj and deads2k June 23, 2017 18:27
@@ -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
Copy link
Contributor

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.

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'
Copy link
Contributor

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

Copy link
Contributor Author

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'.

Copy link
Contributor

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

Copy link
Contributor Author

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.

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'
Copy link
Contributor

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.

Copy link
Contributor

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.

@enj
Copy link
Contributor

enj commented Jun 23, 2017

@openshift/cli-review

@@ -371,9 +381,16 @@ subjectCheck:
}

if isUpdate {
if o.RoleBindingName != "" {
Copy link
Contributor

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

@@ -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)
Copy link
Contributor

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

Copy link
Contributor Author

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().

ret := make([]*authorizationapi.RoleBinding, 0)

for _, currBinding := range bindings {
if currBinding.RoleRef.Name == role && (rb == "" || rb == currBinding.Name) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: len(rb) == 0

@@ -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")
Copy link
Contributor

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/overrides/appends to/

Copy link
Contributor

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a few cases of this, such as in new-app, rsh, and new-build

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -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)
Copy link
Contributor

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.

@@ -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")
Copy link
Contributor

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.

@mrogers950
Copy link
Contributor Author

  • Replaced cmd tests with unit tests
  • Use default role name behavior for deletes
  • Use only --rolebinding-name not -N
  • Revised option description, other nits.

@@ -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)
Copy link
Contributor

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/

@@ -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) {
Copy link
Contributor

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

@mrogers950
Copy link
Contributor Author

Changed to filtering by name in AddRole(), leaving GetExistingRoleBindingsForRole() as-is.

Copy link
Contributor

@stevekuznetsov stevekuznetsov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems fine to me but @enj / @deads2k should look

@@ -371,9 +390,16 @@ subjectCheck:
}

if isUpdate {
if len(o.RoleBindingName) > 0 {
roleBinding.Name = o.RoleBindingName
Copy link
Contributor

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

@@ -336,6 +342,19 @@ func (o *RoleModificationOptions) AddRole() error {
if err != nil {
Copy link
Contributor

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

@@ -336,6 +342,19 @@ func (o *RoleModificationOptions) AddRole() error {
if err != nil {
return err
}

if len(o.RoleBindingName) > 0 {
filteredRoleBindings := []*authorizationapi.RoleBinding{}
Copy link
Contributor

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

roleBindings = filteredRoleBindings
}
}

roleBindingNames, err := o.RoleBindingAccessor.GetExistingRoleBindingNames()
Copy link
Contributor

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

err = o.RoleBindingAccessor.UpdateRoleBinding(roleBinding)
} else {
roleBinding.Name = getUniqueName(o.RoleName, roleBindingNames)
if len(o.RoleBindingName) > 0 {
Copy link
Contributor

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

@mrogers950
Copy link
Contributor Author

@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.

isUpdate := false
if len(o.RoleBindingName) > 0 {
// Look for an existing rolebinding by name.
roleBinding, err = o.RoleBindingAccessor.GetRoleBinding(o.RoleNamespace, o.RoleBindingName)
Copy link
Contributor

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

// 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 {
Copy link
Contributor

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

@@ -165,6 +166,21 @@ func (a LocalRoleBindingAccessor) GetExistingRoleBindingNames() (*sets.String, e
return ret, nil
}

func (a LocalRoleBindingAccessor) GetRoleBinding(roleNamespace, roleBindingName string) (*authorizationapi.RoleBinding, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespace, name string

@@ -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{})
Copy link
Contributor

@liggitt liggitt Jul 12, 2017

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{})?

@@ -222,6 +238,22 @@ func (a ClusterRoleBindingAccessor) GetExistingRoleBindingNames() (*sets.String,
return ret, nil
}

func (a ClusterRoleBindingAccessor) GetRoleBinding(roleNamespace, roleBindingName string) (*authorizationapi.RoleBinding, error) {
Copy link
Contributor

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

Copy link
Contributor

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

@@ -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{})
Copy link
Contributor

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{})?

Copy link
Contributor

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

@enj
Copy link
Contributor

enj commented Aug 8, 2017

/test end_to_end

@mrogers950
Copy link
Contributor Author

After Mo's in-person feedback, I've refactored the revised AddRole logic and simplified the tests. Also rebased.

@mrogers950
Copy link
Contributor Author

/test extended_conformance_install_update

@mrogers950
Copy link
Contributor Author

Flake #11114

@mrogers950
Copy link
Contributor Author

/retest

@mrogers950 mrogers950 force-pushed the rb-add-name branch 2 times, most recently from 5a26326 to 79714d0 Compare August 22, 2017 19:12
Matt Rogers added 2 commits August 24, 2017 16:03
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]>
@enj
Copy link
Contributor

enj commented Aug 24, 2017

/lgtm

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Aug 24, 2017
@enj
Copy link
Contributor

enj commented Aug 24, 2017

@fabianofranz can you approve this?

@mrogers950
Copy link
Contributor Author

flake #13966

@mrogers950
Copy link
Contributor Author

/retest

@juanvallejo
Copy link
Contributor

/test extended_conformance_install_update

/lgtm

@openshift-merge-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: enj, juanvallejo, mrogers950
We suggest the following additional approver: liggitt

Assign the PR to them by writing /assign @liggitt in a comment when ready.

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 /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@mrogers950 mrogers950 changed the title Add a -N flag to rolebinding commands Add the --rolebinding-name option to rolebinding commands Aug 29, 2017
@enj enj added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 29, 2017
@enj
Copy link
Contributor

enj commented Aug 29, 2017

Approving based on feedback from @juanvallejo

@mrogers950
Copy link
Contributor Author

/retest

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

1 similar comment
@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-merge-robot
Copy link
Contributor

Automatic merge from submit-queue

@openshift-merge-robot openshift-merge-robot merged commit 1d698c8 into openshift:master Sep 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants