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

Rework users.GetUID #4262

Merged
merged 8 commits into from
Sep 6, 2024
Merged

Rework users.GetUID #4262

merged 8 commits into from
Sep 6, 2024

Conversation

twz123
Copy link
Member

@twz123 twz123 commented Apr 9, 2024

Description

Rename to LookupUID
The new name more closely matches the standard library function name. Also, "lookup" sounds less lightweight than "get", which gives a better sense of the actual runtime cost.

Explicit usage and fallback handling
Prefer to call utility functions directly with a UID, rather than having them accept a username and call LookupUID internally. This allows for a more obvious fallback handling in case certain users don't exist. Add a special RootUID constant that can be used to explicitly fallback to the root user. Also add log statements to every fallback assignment. Use -1 as UID return value in case of errors. The zero value for a UID happens to be root's UID. Assuming root may or may not be a good default, depending on the use case. Setting file ownership to root is a restrictive and safe default, running programs as root is not. By returning UnknownUID as a constant to -1, callers must explicitly decide on the fallback instead of accidentally assuming root.

Use id program from PATH
The id program is not always installed to /usr/bin. Rely on k0s's PATH to find the right executable.

More context for errors
There are quite some failure modes. Sort them out by providing hopefully helpful error messages. This might help when debugging things. Introduce users.ErrNotFound, which makes the errors.Is() checks nicer. On the contrary, the error message won't contain the user name anymore. Wrap the error accordingly on the caller side instead.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

How Has This Been Tested?

  • Manual test
  • Auto test added

Checklist:

  • My code follows the style guidelines of this project
  • My commit messages are signed-off
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

@twz123 twz123 added the chore label Apr 9, 2024
var uid string

if entry, err := user.Lookup(name); err != nil {
if !errors.Is(err, user.UnknownUserError(name)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

errors.Is(err, user.UnknownUserError(name)) looks weird but seems to work.

Maybe returning a locally defined UnknownUserError = errors.New("unknown user") would make the function more pleasant to use.

https://go.dev/play/p/EsG_Q7Uagbw

Copy link
Member Author

Choose a reason for hiding this comment

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

I was also pondering about this. The original code re-used this standard library error in this way. Changing the returned error would trickle down to callers, which are relying on this as well.

What about having a function for this instead, e.g.

func IsNotExist(err error) bool {
	var UnknownUserError user.UnknownUserError
	return errors.As(err, &UnknownUserError)
}

Callers could then use if users.IsNotExist(err) { ... } ... Which would mimic the design of the standard library's os.Is... functions.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds reasonable. The os.Is... function docs now do say new code should use errors.Is(err, fs.ErrNotExist) but they're not going anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh. Right. I'll have a look at your initial proposal first, then.

Copy link
Member Author

Choose a reason for hiding this comment

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

PTAL

@twz123 twz123 marked this pull request as ready for review April 10, 2024 07:53
@twz123 twz123 requested a review from a team as a code owner April 10, 2024 07:53
@twz123 twz123 requested review from kke and jnummelin April 10, 2024 07:53
Copy link
Contributor

This pull request has merge conflicts that need to be resolved.

Copy link
Contributor

This pull request has merge conflicts that need to be resolved.

Copy link
Contributor

This pull request has merge conflicts that need to be resolved.

Copy link
Contributor

This pull request has merge conflicts that need to be resolved.

Comment on lines 61 to 67
firstLine, _, _ := bytes.Cut(exitErr.Stderr, []byte{'\n'})
return UnknownUID, fmt.Errorf("%w (%w: %s)", err, idErr, string(firstLine))
}
return UnknownUID, fmt.Errorf("%w (%w)", err, idErr)
}

uid = string(bytes.TrimSuffix(out, []byte{'\n'}))
Copy link
Contributor

@kke kke Jun 5, 2024

Choose a reason for hiding this comment

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

Two different ways to get the first line here :) Maybe the stderr shouldn't be cut anyway?

Copy link
Member Author

Choose a reason for hiding this comment

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

To be precise, this removes a trailing newline, and doesn't return only the first line, but all of them, if any. id is expected to only write a single line to stdout, though.

The stderr truncation is different. In case of an error, we never know how many lines will be written. Having only the first line in the error message seemed like a good idea to me, to not potentially pollute the error message but still be helpful. The standard error message of *exec.ExitError is not overly helpful on its own, it's usually just "exit status 1" and that's it.

Copy link
Member Author

Choose a reason for hiding this comment

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

PTAL

@twz123 twz123 force-pushed the explicit-getuid branch from d03984f to 1676c13 Compare July 3, 2024 15:48
Copy link
Contributor

github-actions bot commented Aug 2, 2024

The PR is marked as stale since no activity has been recorded in 30 days

@github-actions github-actions bot added the Stale label Aug 2, 2024
@twz123 twz123 removed the Stale label Aug 3, 2024
twz123 added 8 commits August 26, 2024 15:11
This allows for a more obvious fallback handling in case certain
users don't exist. Add a special RootUID constant that can be used
to explicitly assign a fallback UID. Also add log statements to every
fallback assignment.

Signed-off-by: Tom Wieczorek <[email protected]>
Signed-off-by: Tom Wieczorek <[email protected]>
There are quite some failure modes for GetUID. Sort them out by
providing hopefully helpful error messages.

Signed-off-by: Tom Wieczorek <[email protected]>
The `id` program is not always installed to /usr/bin. Rely on k0s's PATH
to find the right executable.

Signed-off-by: Tom Wieczorek <[email protected]>
The new name more closely matches the standard library function name.
Also, "lookup" sounds less lightweight than "get", which gives a better
sense of the actual runtime cost.

Signed-off-by: Tom Wieczorek <[email protected]>
The zero value for a UID happens to be root's UID. Assuming root may
or may not be a good default, depending on the use case. Setting file
ownership to root is a restrictive and safe default, running programs as
root is not. By returning "UnknownUID" as a constant to -1, callers must
explicitly decide on the fallback instead of accidentally assuming root.

Signed-off-by: Tom Wieczorek <[email protected]>
The etcd user ID is already stored in the etcd struct, so it can be
reused.

Signed-off-by: Tom Wieczorek <[email protected]>
Makes the errors.Is() checks nicer. On the contrary, the error message
won't contain the user name anymore. Wrap the error accordingly on the
caller side instead.

Signed-off-by: Tom Wieczorek <[email protected]>
@twz123 twz123 requested a review from kke August 26, 2024 13:46
Copy link
Member

@jnummelin jnummelin left a comment

Choose a reason for hiding this comment

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

LGTM

@twz123 twz123 merged commit c9fc420 into k0sproject:main Sep 6, 2024
83 checks passed
@twz123 twz123 deleted the explicit-getuid branch September 6, 2024 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants