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

osutil: pass --extrausers option to groupdel #10363

Merged
merged 6 commits into from
Jun 21, 2021

Conversation

mardy
Copy link
Contributor

@mardy mardy commented Jun 9, 2021

Do not shy off from deleting groups using the --extrausers option, if
needed.

See https://bugs.launchpad.net/bugs/1840375

Needs someone (@pedronis ?) to decide what to do on systems where groupdel does not support the --extrausers option: this branch currently silently ignores the error (meaning that groupdel has failed but the user is not aware of it), because this leads to the same outcome we had before this branch. But we might want to return an error instead?

Do not shy off from deleting groups using the `--extrausers` option, if
needed.

See https://bugs.launchpad.net/bugs/1840375
@mardy mardy added the Simple 😃 A small PR which can be reviewed quickly label Jun 9, 2021
@mardy
Copy link
Contributor Author

mardy commented Jun 9, 2021

Well, actually, the code is returning an error in any case here, so I guess we can simplify the code and always return the error describing the groupdel failure.

Copy link
Contributor

@anonymouse64 anonymouse64 left a comment

Choose a reason for hiding this comment

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

lgtm, but I think we could at least log in the case where it fails with the --extrausers flag

osutil/user.go Outdated
if output2, err2 := cmd.CombinedOutput(); err2 != nil {
if extraUsers && bytes.Contains(output2, []byte("unrecognized option")) {
// We have hit https://bugs.launchpad.net/bugs/1840375
// TODO: decide what to do.
Copy link
Contributor

Choose a reason for hiding this comment

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

probably IMHO we should just log an error and return nil. The previous code gave no indication anything was wrong, whereas if we at least log the user will see that some part of the group deletion failed.

osutil/user.go Outdated
if output2, err2 := cmd.CombinedOutput(); err2 != nil {
if extraUsers && bytes.Contains(output2, []byte("unrecognized option")) {
// We have hit https://bugs.launchpad.net/bugs/1840375
return fmt.Errorf("groupdel does not support '--extrausers' option (after %s)", useraddErrStr)
Copy link
Contributor

Choose a reason for hiding this comment

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

after reads a bit weird here, not sure the right way to phrase it is, but usually we will use the pattern while doing x, error foo happened, but really these two errors are orthogonal to each other IMHO, so maybe the thing to do here is to make them separate errors, one each line:

errors encountered ensuring group foo exists:
- groupdel does not support --extrausers option
- useradd failed with foo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've read about go's support for nested errors (with the %w modifier passed to Errorf); do you think it makes sense to use it here?

Copy link
Contributor

Choose a reason for hiding this comment

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

As we discussed in the meeting, %w is only meant to be used in the case where callers want to inspect an error type, if we did something simple like:

return fmt.Errorf("some syscall failed while doing foo: %v",err)

then if err was say a os.PathError, the standard library says you can check for that specific error with os.IsNotExists, but now we created a new error type from Errorf which doesn't match the os.PathError, so that check would fail. The solution from the Go team to this was to introduce %w which wraps errors so that if we did this:

return fmt.Errorf("some syscall failed while doing foo: %w",err)

now the error returned by Errorf will be wrapped such that the os.IsNotExists function would return true since the IsNotExists function now can use the Unwrap() and As() or Is() functions from go's errors package (see https://golang.org/pkg/errors/).

As was pointed out though, since we use an old go version for some distros, we cannot use the errors package directly, and instead we have to do sneaky tricks like use the xerrors package and define our format strings as constants, since old Go does not know about the %w verb. See here for an example: https://github.com/snapcore/snapd/blob/master/bootloader/lkenv/lkenv.go#L264-L275

Copy link
Contributor

Choose a reason for hiding this comment

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

but also as explained wrapped errors is not what we want here since:

  • the callers of this function probably don't care to inspect the different error cases
  • the errors are not related to each other

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Copy link
Contributor

@anonymouse64 anonymouse64 left a comment

Choose a reason for hiding this comment

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

+1 from me now on the error message, but bonus points if you fix the existing confusing error message too 😄

osutil/user.go Outdated
return fmt.Errorf(`errors encountered ensuring user %s exists:
- %s
- groupdel does not support '--extrausers' option`, name, useraddErrStr)
} else {
return fmt.Errorf("groupdel failed with: %s (after %s)", OutputErr(output2, err2), useraddErrStr)
Copy link
Contributor

Choose a reason for hiding this comment

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

not a big deal, but would be nice to change this error message too, I just noticed that it was like this originally that's probably where you got your error message format from 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed it was :-)

I updated the branch now.

@mardy mardy added the Squash-merge Please squash this PR when merging. label Jun 15, 2021
Copy link
Contributor

@anonymouse64 anonymouse64 left a comment

Choose a reason for hiding this comment

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

still LGTM, thanks for fixing that other error message

Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Looks fine, just one suggestion for simplification

osutil/user.go Outdated
cmd = exec.Command(delCmdStr[0], delCmdStr[1:]...)
if output2, err2 := cmd.CombinedOutput(); err2 != nil {
var groupdelErrStr string
if extraUsers && bytes.Contains(output2, []byte("unrecognized option")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is tricky - if this code runs localized we will not detect the error. We could always run userdel with "LC_ALL=C.UTF-8" but then the error could be confusing to the user. My feeling is we should just remove the if extraUsers special case and always create the "OutputErr". I.e. something like:

diff --git a/osutil/user.go b/osutil/user.go
index 851d268033..6ebf45e714 100644
--- a/osutil/user.go
+++ b/osutil/user.go
@@ -20,7 +20,6 @@
 package osutil
 
 import (
-       "bytes"
        "fmt"
        "os"
        "os/exec"
@@ -154,13 +153,7 @@ func EnsureUserGroup(name string, id uint32, extraUsers bool) error {
                delCmdStr = append(delCmdStr, name)
                cmd = exec.Command(delCmdStr[0], delCmdStr[1:]...)
                if output2, err2 := cmd.CombinedOutput(); err2 != nil {
-                       var groupdelErrStr string
-                       if extraUsers && bytes.Contains(output2, []byte("unrecognized option")) {
-                               // We have hit https://bugs.launchpad.net/bugs/1840375
-                               groupdelErrStr = "groupdel does not support '--extrausers' option"
-                       } else {
-                               groupdelErrStr = OutputErr(output2, err2).Error()
-                       }
+                       groupdelErrStr := OutputErr(output2, err2).Error()
                        return fmt.Errorf(`errors encountered ensuring user %s exists:
 - %s
 - %s`, name, useraddErrStr, groupdelErrStr)
diff --git a/osutil/user_test.go b/osutil/user_test.go
index 3227670025..996f899137 100644
--- a/osutil/user_test.go
+++ b/osutil/user_test.go
@@ -567,7 +567,7 @@ func (s *ensureUserSuite) TestEnsureUserGroupFailedUseraddCoreNoExtra(c *check.C
        err := osutil.EnsureUserGroup("lakatos", 123456, true)
        c.Assert(err, check.ErrorMatches, `errors encountered ensuring user lakatos exists:
 - useradd failed with: some error
-- groupdel does not support '--extrausers' option`)
+- groupdel: unrecognized option '--extrauser'`)
 
        c.Check(mockGroupDel.Calls(), check.DeepEquals, [][]string{
                {"groupdel", "--extrausers", "lakatos"},

and it seems the error is still readable enough(?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, right! I could just search for the --extrausers string, which should be printed regardless of the locale, but indeed all this if block is a bit too much for just getting a slightly better error message. So I'll happily apply your suggestion :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

@mvo5 mvo5 merged commit c48ab72 into canonical:master Jun 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Simple 😃 A small PR which can be reviewed quickly Squash-merge Please squash this PR when merging.
Projects
None yet
3 participants