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 API to create user settings #7095
Conversation
5aba1a8
to
3b16941
Compare
8a50fca
to
d9675c1
Compare
Note for myself, we can fetch the current user uid with this command: oc get users.user.openshift.io/~ -o template={{.metadata.uid}} |
519d7f2
to
3c44a04
Compare
93763b1
to
643e34b
Compare
35c212d
to
1b8b14c
Compare
/retest |
pkg/usersettings/handlers.go
Outdated
|
||
// Fetch the current user with auth.User token and returning the user-setting ConfigMap. | ||
func (h *UserSettingsHandler) getUserSettings(user *auth.User) (*core.ConfigMap, error) { | ||
context := context.TODO() |
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.
lets just move the context initialization to the HandleUserSettings()
and pass it as parameter. That way if we decide in the future to replace it by a single context there would be less changes.
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 moved the context, createServiceAccountClient and getResourceData now at the top of HandleUserSettings
so that there is just one place for it
pkg/usersettings/handlers.go
Outdated
// Fetch the current user with auth.User token and returning the user-setting ConfigMap. | ||
func (h *UserSettingsHandler) getUserSettings(user *auth.User) (*core.ConfigMap, error) { | ||
context := context.TODO() | ||
resourceData, err := h.getResourceData(context, user) |
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.
Also this call can be moved to the HandleUserSettings()
and pas as param to the appropriate UserSettingsHandler
method
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 thought this also, but are unsure how an OPTION request would handle this. Should I add extra if for an OPTION request before that or should we check if this works locally and on a cluster. I will change this and we see what happen :)
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.
Moved context, createServiceAccountClient and getResourceData to HandleUserSettings
pkg/usersettings/handlers.go
Outdated
}, | ||
} | ||
|
||
client, err := h.createServiceAccountClient() |
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.
Also this call can be moved to the HandleUserSettings()
and pas as param to the appropriate UserSettingsHandler
method.
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.
Done
pkg/usersettings/types.go
Outdated
ResourceSuffix string | ||
} | ||
|
||
func (r *ResourceData) getConfigMapName() string { |
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.
Would be good to add unit tests for these methods.
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 would like to move this from "name getters / helpers" to the moment when creating the ResourceNames. See comment above.
But for the moment I added tests for the "name getters / helpers" here as well.
pkg/usersettings/handlers.go
Outdated
}, | ||
} | ||
|
||
roleBinding := &rbac.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.
Lets create a helper for this, so we can add unit-test.
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.
Done, see helpers.go and helpers_test.go
pkg/usersettings/handlers.go
Outdated
return nil, err | ||
} | ||
|
||
role := &rbac.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.
Lets create a helper for this, so we can add unit-test.
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.
Done, see helpers.go and helpers_test.go
pkg/usersettings/handlers.go
Outdated
}, | ||
} | ||
|
||
configMap := &core.ConfigMap{ |
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.
Lets create a helper for this, so we can add unit-test.
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.
Done, see helpers.go and helpers_test.go
@@ -543,29 +543,28 @@ func main() { | |||
bridge.FlagFatalf("user-auth", "must be one of: oidc, openshift, disabled") | |||
} | |||
|
|||
var resourceListerToken string |
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.
👍
@jhadvig All done, I had one small idea for another cleanup. And I can squash all commits leter. Thanks for reviewing this. |
pkg/usersettings/helpers.go
Outdated
if name == "kube:admin" { | ||
resourceIdentifier = "kubeadmin" | ||
} else if uid != "" { | ||
resourceIdentifier = uid |
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.
Make sure there is no UID for the kubeadmin case:
if name == "kube:admin" { | |
resourceIdentifier = "kubeadmin" | |
} else if uid != "" { | |
resourceIdentifier = uid | |
if uid != "" { | |
resourceIdentifier = uid | |
} else if name == "kube:admin" { | |
resourceIdentifier = "kubeadmin" |
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.
Changed.
} | ||
case http.MethodPost: | ||
configMap, err := h.createUserSettings(context, serviceAccountClient, userSettingMeta) | ||
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.
The preferred idiomatic way for go, how to structure the response should be:
if err != nil {
errMsg := fmt.Sprintf("Failed to create user settings: %v", err)
klog.Errorf(errMsg)
serverutils.SendResponse(w, http.StatusBadGateway, serverutils.ApiError{Err: errMsg})
return
}
serverutils.SendResponse(w, http.StatusOK, configMap)
so we return immediatelly
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.
done
return nil, err | ||
} | ||
|
||
userInfo, err := client.Resource(USER_RESOURCE).Get(context, "~", meta.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.
yeah, thats probably ok
|
||
_, err = client.RbacV1().RoleBindings(namespace).Create(ctx, roleBinding, meta.CreateOptions{}) | ||
if err != nil && !apierrors.IsAlreadyExists(err) { | ||
return nil, err |
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.
we should call deleteUserSettings()
method upon error. So we dont end up in an inconsistent state, where Role and RoleBinding was created but the CM for some reason was not.
Either that or we rely that the request would be repeated until all the resources are created.
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.
As discussed in slack, I added deletedUserSettings
before returning the create error.
4e977b8
to
2fcb14d
Compare
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.
/lgtm
/assign @spadgett |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: jerolimov, jhadvig, spadgett The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@jerolimov: The following test failed, say
Full PR test history. Your PR dashboard. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
/retest Please review the full test history for this PR and help us cut down flakes. |
Fixes:
https://issues.redhat.com/browse/ODC-4755
Analysis / Root cause:
Based on the User Settings for OpenShift Console enhancement proposal we want to create a
ConfigMap
for each individual user in the namespaceopenshift-console-user-settings
Currently you need create this namespace yourself, see Test Setup below. This namespace will be automatically created by the console-operator, see PR openshift/console-operator#479
Solution Description:
Because the user could not create a ConfigMap here, we create a new
ConfigMap
,Role
andRoleBinding
with the service account role over the bridge. This code provides 3 new endpoints to create, get and delete the user settings. (The second and third API calls primary for debugging / development.) The console will fetch the ConfigMap over the known k8sWatch API.API
GET /api/console/user-settings
Get the current user settings (returns a ConfigMap or 404)
POST /api/console/user-settings
Create a user setting ConfigMap for the current user. It returns the existing ConfigMap if it is already exist. This method does not accept an initial content for the ConfigMap.
DELETE /api/console/user-settings
Delete the user setting ConfigMap for the current user. Returns
204 No Content
if the ConfigMap was deleted, also if it was not created yet.Screen shots / Gifs for design review:
Irrelevant
Unit test coverage report:
➜ console git:([odc-4755](https://issues.redhat.com/browse/odc-4755)) go test -coverprofile=coverage.out github.com/openshift/console/pkg/usersettings ok github.com/openshift/console/pkg/usersettings 0.006s coverage: 16.2% of statements ➜ console git:([odc-4755](https://issues.redhat.com/browse/odc-4755)) ✗ go tool cover -func=coverage.out github.com/openshift/console/pkg/usersettings/handlers.go:37: HandleUserSettings 0.0% github.com/openshift/console/pkg/usersettings/handlers.go:86: getUserSettings 0.0% github.com/openshift/console/pkg/usersettings/handlers.go:92: createUserSettings 0.0% github.com/openshift/console/pkg/usersettings/handlers.go:121: deleteUserSettings 0.0% github.com/openshift/console/pkg/usersettings/handlers.go:140: createServiceAccountClient 0.0% github.com/openshift/console/pkg/usersettings/handlers.go:149: createUserProxyClient 0.0% github.com/openshift/console/pkg/usersettings/handlers.go:158: getUserSettingMeta 0.0% github.com/openshift/console/pkg/usersettings/helpers.go:11: getUserSettingMeta 100.0% github.com/openshift/console/pkg/usersettings/helpers.go:29: createRole 100.0% github.com/openshift/console/pkg/usersettings/helpers.go:61: createRoleBinding 100.0% github.com/openshift/console/pkg/usersettings/helpers.go:85: createConfigMap 100.0% github.com/openshift/console/pkg/usersettings/types.go:10: getConfigMapName 100.0% github.com/openshift/console/pkg/usersettings/types.go:14: getRoleName 100.0% github.com/openshift/console/pkg/usersettings/types.go:18: getRoleBindingName 100.0% total: (statements) 16.2%
Test setup:
This PR require some additional RBAC rules.
1a Create namespace:
Until https://github.com/openshift/console-operator/pull/486/files is merged and available on your cluster you need to add this as well:
1b Create Role:
1c Create RoleBinding:
In frontend code this is automatically done by
coFetch
, see co-fetch.js. But to test this without additional frontend code or postman I extracted the code for you:2a Extract crcf token:
2b Get user-settings:
2c Create user-settings:
Browser conformance:
Irrelevant