Merge lp:~thumper/juju-core/local-sudo-caller into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 1465
Proposed branch: lp:~thumper/juju-core/local-sudo-caller
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 182 lines (+91/-18)
3 files modified
environs/local/config.go (+34/-18)
environs/local/config_test.go (+45/-0)
environs/local/export_test.go (+12/-0)
To merge this branch: bzr merge lp:~thumper/juju-core/local-sudo-caller
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+174904@code.launchpad.net

Commit message

Add some extra tests around the sudo checks.

Encapsulate the sudo checks, and make a function to
get the uid and gid for the user.

https://codereview.appspot.com/11321043/

Description of the change

Add some extra tests around the sudo checks.

Encapsulate the sudo checks, and make a function to
get the uid and gid for the user.

https://codereview.appspot.com/11321043/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Reviewers: mp+174904_code.launchpad.net,

Message:
Please take a look.

Description:
Add some extra tests around the sudo checks.

Encapsulate the sudo checks, and make a function to
get the uid and gid for the user.

https://code.launchpad.net/~thumper/juju-core/local-sudo-caller/+merge/174904

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/11321043/

Affected files:
   A [revision details]
   M environs/local/config.go
   M environs/local/config_test.go
   M environs/local/export_test.go

Revision history for this message
Ian Booth (wallyworld) wrote :

LGTM

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go
File environs/local/config.go (right):

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode16
environs/local/config.go:16: var rootCheckFunction = func() bool {
Perhaps checkRoot() is better?

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode86
environs/local/config.go:86: // change ownership of the directories.
I don't think the above comment belongs here

https://codereview.appspot.com/11321043/

Revision history for this message
John A Meinel (jameinel) wrote :

LGTM

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go
File environs/local/config.go (right):

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode16
environs/local/config.go:16: var rootCheckFunction = func() bool {
On 2013/07/16 03:56:35, wallyworld wrote:
> Perhaps checkRoot() is better?

checkIfRoot ?

https://codereview.appspot.com/11321043/diff/1/environs/local/config_test.go
File environs/local/config_test.go (right):

https://codereview.appspot.com/11321043/diff/1/environs/local/config_test.go#newcode87
environs/local/config_test.go:87: defer
local.SetRootCheckFunction(rootCheck)
I would probably label this origRootCheck or something to that effect,
so it is immediately clear that this was the value before setting it
without having to dig up the return value of SetRootCheckFunction.

https://codereview.appspot.com/11321043/diff/1/environs/local/export_test.go
File environs/local/export_test.go (right):

https://codereview.appspot.com/11321043/diff/1/environs/local/export_test.go#newcode17
environs/local/export_test.go:17: }
This is fine as is, though for testing stuff I do still prefer the style
of

return func() {
  rootCheckFunction = old
}

But if you ever want to inspect what the real root check does, I suppose
this is fine, too.

https://codereview.appspot.com/11321043/

Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM with a few minor suggestions and general support of john's remarks,
thought i don't mind much.

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go
File environs/local/config.go (right):

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode16
environs/local/config.go:16: var rootCheckFunction = func() bool {
On 2013/07/16 06:55:50, jameinel wrote:
> On 2013/07/16 03:56:35, wallyworld wrote:
> > Perhaps checkRoot() is better?

> checkIfRoot ?

isRoot? (or even "amRoot")

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode81
environs/local/config.go:81: // if the values have been set. If the
values haven't been set, then
this reads slightly ambiguously to me.

perhaps:

// getSudoCallerIds returns the user id and group id of the SUDO caller.
// If either is unset, it returns zero for both values.
// An error is returned if the relevant environment variables
// are not valid integers.
?

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode84
environs/local/config.go:84: func getSudoCallerIds() (uid, gid int, err
error) {
sudoCallerIds ?

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode92
environs/local/config.go:92: logger.Errorf("expected %q for SUDO_UID to
be an int: %v", uidStr, err)
do we need to log this error here? wouldn't
it be better to return a better error which
will be surely be logged later?

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode98
environs/local/config.go:98: uid = 0 // clear out any value we may have
i think that rather than messing around assigning to the return values,
i'd do:

if uidStr == "" || gidStr == "" {
     return 0, 0, nil
}
uid, err := strconv.Atoi(...)
if err != nil {
    return 0, 0, fmt.Errorf("invalid value %q for SUDO_UID", gidStr)
}
gid, err := stdconv.Atoi(...)
if err != nil {
    return 0, 0, fmt.Errorf("similar...")
}
return uid, gid, nil

this keeps things a bit more obvious, with better error messages
to boot (the error from Atoi is rarely useful)

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode126
environs/local/config.go:126: if info.IsDir() && err == nil {
if err == nil && info.IsDir() {

(if there's an error, the info may be nil)

although in fact, i think:

if info != nil && info.IsDir() {

is probably better still.
not that any of this is likely to happen running as root.

https://codereview.appspot.com/11321043/

Revision history for this message
Frank Mueller (themue) wrote :

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go
File environs/local/config.go (right):

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode17
environs/local/config.go:17: return os.Getuid() == 0
Any need why this is a variable?

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode98
environs/local/config.go:98: uid = 0 // clear out any value we may have
On 2013/07/16 13:30:01, rog wrote:
> i think that rather than messing around assigning to the return
values,
> i'd do:

> if uidStr == "" || gidStr == "" {
> return 0, 0, nil
> }
> uid, err := strconv.Atoi(...)
> if err != nil {
> return 0, 0, fmt.Errorf("invalid value %q for SUDO_UID", gidStr)
> }
> gid, err := stdconv.Atoi(...)
> if err != nil {
> return 0, 0, fmt.Errorf("similar...")
> }
> return uid, gid, nil

> this keeps things a bit more obvious, with better error messages
> to boot (the error from Atoi is rarely useful)

+1

https://codereview.appspot.com/11321043/

Revision history for this message
Tim Penhey (thumper) wrote :
Download full text (4.5 KiB)

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go
File environs/local/config.go (right):

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode16
environs/local/config.go:16: var rootCheckFunction = func() bool {
On 2013/07/16 13:30:01, rog wrote:
> On 2013/07/16 06:55:50, jameinel wrote:
> > On 2013/07/16 03:56:35, wallyworld wrote:
> > > Perhaps checkRoot() is better?
> >
> >
> > checkIfRoot ?

> isRoot? (or even "amRoot")

Went with checkIfRoot

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode17
environs/local/config.go:17: return os.Getuid() == 0
On 2013/07/16 14:14:35, mue wrote:
> Any need why this is a variable?

Yes, we need to override it for tests.

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode81
environs/local/config.go:81: // if the values have been set. If the
values haven't been set, then
On 2013/07/16 13:30:01, rog wrote:
> this reads slightly ambiguously to me.

> perhaps:

> // getSudoCallerIds returns the user id and group id of the SUDO
caller.
> // If either is unset, it returns zero for both values.
> // An error is returned if the relevant environment variables
> // are not valid integers.
> ?

Shamelessly copied.

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode84
environs/local/config.go:84: func getSudoCallerIds() (uid, gid int, err
error) {
On 2013/07/16 13:30:01, rog wrote:
> sudoCallerIds ?

You really don't like "get" do you?

Renamed.

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode86
environs/local/config.go:86: // change ownership of the directories.
On 2013/07/16 03:56:35, wallyworld wrote:
> I don't think the above comment belongs here

No it isn't. Deleted.

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode92
environs/local/config.go:92: logger.Errorf("expected %q for SUDO_UID to
be an int: %v", uidStr, err)
On 2013/07/16 13:30:01, rog wrote:
> do we need to log this error here? wouldn't
> it be better to return a better error which
> will be surely be logged later?

Cleaned up like below.

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode98
environs/local/config.go:98: uid = 0 // clear out any value we may have
On 2013/07/16 14:14:35, mue wrote:
> On 2013/07/16 13:30:01, rog wrote:
> > i think that rather than messing around assigning to the return
values,
> > i'd do:
> >
> > if uidStr == "" || gidStr == "" {
> > return 0, 0, nil
> > }
> > uid, err := strconv.Atoi(...)
> > if err != nil {
> > return 0, 0, fmt.Errorf("invalid value %q for SUDO_UID", gidStr)
> > }
> > gid, err := stdconv.Atoi(...)
> > if err != nil {
> > return 0, 0, fmt.Errorf("similar...")
> > }
> > return uid, gid, nil
> >
> > this keeps things a bit more obvious, with better error messages
> > to boot (the error from Atoi is rarely useful)

> +1

Much nicer.

https://codereview.appspot.com/11321043/diff/1/environs/local/config.go#newcode126
environs/local/config.go:126: if info.IsDir() && err == nil {
On 2013/07/16 13:30:01, rog wrote:
> if err == nil && info.IsDir() {

> (if there's an error,...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'environs/local/config.go'
--- environs/local/config.go 2013-07-07 22:34:06 +0000
+++ environs/local/config.go 2013-07-16 21:59:25 +0000
@@ -13,6 +13,10 @@
13 "launchpad.net/juju-core/schema"13 "launchpad.net/juju-core/schema"
14)14)
1515
16var checkIfRoot = func() bool {
17 return os.Getuid() == 0
18}
19
16var configChecker = schema.StrictFieldMap(20var configChecker = schema.StrictFieldMap(
17 schema.Fields{21 schema.Fields{
18 "root-dir": schema.String(),22 "root-dir": schema.String(),
@@ -31,9 +35,8 @@
3135
32func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig {36func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig {
33 user := os.Getenv("USER")37 user := os.Getenv("USER")
34 root := false38 root := checkIfRoot()
35 if user == "root" {39 if root {
36 root = true
37 sudo_user := os.Getenv("SUDO_USER")40 sudo_user := os.Getenv("SUDO_USER")
38 if sudo_user != "" {41 if sudo_user != "" {
39 user = sudo_user42 user = sudo_user
@@ -74,6 +77,28 @@
74 return filepath.Join(c.rootDir(), filename)77 return filepath.Join(c.rootDir(), filename)
75}78}
7679
80// sudoCallerIds returns the user id and group id of the SUDO caller.
81// If either is unset, it returns zero for both values.
82// An error is returned if the relevant environment variables
83// are not valid integers.
84func sudoCallerIds() (int, int, error) {
85 uidStr := os.Getenv("SUDO_UID")
86 gidStr := os.Getenv("SUDO_GID")
87
88 if uidStr == "" || gidStr == "" {
89 return 0, 0, nil
90 }
91 uid, err := strconv.Atoi(uidStr)
92 if err != nil {
93 return 0, 0, fmt.Errorf("invalid value %q for SUDO_UID", uidStr)
94 }
95 gid, err := strconv.Atoi(gidStr)
96 if err != nil {
97 return 0, 0, fmt.Errorf("invalid value %q for SUDO_GID", gidStr)
98 }
99 return uid, gid, nil
100}
101
77func (c *environConfig) createDirs() error {102func (c *environConfig) createDirs() error {
78 for _, dirname := range []string{103 for _, dirname := range []string{
79 c.sharedStorageDir(),104 c.sharedStorageDir(),
@@ -88,23 +113,14 @@
88 if c.runningAsRoot {113 if c.runningAsRoot {
89 // If we have SUDO_UID and SUDO_GID, start with rootDir(), and114 // If we have SUDO_UID and SUDO_GID, start with rootDir(), and
90 // change ownership of the directories.115 // change ownership of the directories.
91 uidStr := os.Getenv("SUDO_UID")116 uid, gid, err := sudoCallerIds()
92 gidStr := os.Getenv("SUDO_GID")117 if err != nil {
93 if uidStr != "" && gidStr != "" {118 return err
94 uid, err := strconv.Atoi(uidStr)119 }
95 if err != nil {120 if uid != 0 || gid != 0 {
96 logger.Errorf("Expected %q for SUDO_UID to be an int: %v", uidStr, err)
97 return err
98 }
99 gid, err := strconv.Atoi(gidStr)
100 if err != nil {
101 logger.Errorf("Expected %q for SUDO_GID to be an int: %v", gidStr, err)
102 return err
103 }
104
105 filepath.Walk(c.rootDir(),121 filepath.Walk(c.rootDir(),
106 func(path string, info os.FileInfo, err error) error {122 func(path string, info os.FileInfo, err error) error {
107 if info.IsDir() && err == nil {123 if info != nil && info.IsDir() {
108 if err := os.Chown(path, uid, gid); err != nil {124 if err := os.Chown(path, uid, gid); err != nil {
109 return err125 return err
110 }126 }
111127
=== modified file 'environs/local/config_test.go'
--- environs/local/config_test.go 2013-07-15 02:58:47 +0000
+++ environs/local/config_test.go 2013-07-16 21:59:25 +0000
@@ -83,6 +83,8 @@
83}83}
8484
85func (s *configSuite) TestNamespaceRootNoSudo(c *gc.C) {85func (s *configSuite) TestNamespaceRootNoSudo(c *gc.C) {
86 restore := local.SetRootCheckFunction(func() bool { return true })
87 defer restore()
86 err := os.Setenv("USER", "root")88 err := os.Setenv("USER", "root")
87 c.Assert(err, gc.IsNil)89 c.Assert(err, gc.IsNil)
88 testConfig := minimalConfig(c)90 testConfig := minimalConfig(c)
@@ -90,6 +92,8 @@
90}92}
9193
92func (s *configSuite) TestNamespaceRootWithSudo(c *gc.C) {94func (s *configSuite) TestNamespaceRootWithSudo(c *gc.C) {
95 restore := local.SetRootCheckFunction(func() bool { return true })
96 defer restore()
93 err := os.Setenv("USER", "root")97 err := os.Setenv("USER", "root")
94 c.Assert(err, gc.IsNil)98 c.Assert(err, gc.IsNil)
95 err = os.Setenv("SUDO_USER", "tester")99 err = os.Setenv("SUDO_USER", "tester")
@@ -99,6 +103,47 @@
99 c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test")103 c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test")
100}104}
101105
106func (s *configSuite) TestSudoCallerIds(c *gc.C) {
107 defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
108 defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))
109 for _, test := range []struct {
110 uid string
111 gid string
112 errString string
113 expectedUid int
114 expectedGid int
115 }{{
116 uid: "",
117 gid: "",
118 }, {
119 uid: "1001",
120 gid: "1002",
121 expectedUid: 1001,
122 expectedGid: 1002,
123 }, {
124 uid: "1001",
125 gid: "foo",
126 errString: `invalid value "foo" for SUDO_GID`,
127 }, {
128 uid: "foo",
129 gid: "bar",
130 errString: `invalid value "foo" for SUDO_UID`,
131 }} {
132 os.Setenv("SUDO_UID", test.uid)
133 os.Setenv("SUDO_GID", test.gid)
134 uid, gid, err := local.SudoCallerIds()
135 if test.errString == "" {
136 c.Assert(err, gc.IsNil)
137 c.Assert(uid, gc.Equals, test.expectedUid)
138 c.Assert(gid, gc.Equals, test.expectedGid)
139 } else {
140 c.Assert(err, gc.ErrorMatches, test.errString)
141 c.Assert(uid, gc.Equals, 0)
142 c.Assert(gid, gc.Equals, 0)
143 }
144 }
145}
146
102type configRootSuite struct {147type configRootSuite struct {
103 baseProviderSuite148 baseProviderSuite
104}149}
105150
=== modified file 'environs/local/export_test.go'
--- environs/local/export_test.go 2013-07-15 02:58:47 +0000
+++ environs/local/export_test.go 2013-07-16 21:59:25 +0000
@@ -10,6 +10,14 @@
1010
11var Provider = provider11var Provider = provider
1212
13// SetRootCheckFunction allows tests to override the check for a root user.
14// The return value is the function to restore the old value.
15func SetRootCheckFunction(f func() bool) func() {
16 old := checkIfRoot
17 checkIfRoot = f
18 return func() { checkIfRoot = old }
19}
20
13// ConfigNamespace returns the result of the namespace call on the21// ConfigNamespace returns the result of the namespace call on the
14// localConfig.22// localConfig.
15func ConfigNamespace(cfg *config.Config) string {23func ConfigNamespace(cfg *config.Config) string {
@@ -35,3 +43,7 @@
35 localConfig.mongoDir(),43 localConfig.mongoDir(),
36 }44 }
37}45}
46
47func SudoCallerIds() (uid, gid int, err error) {
48 return sudoCallerIds()
49}

Subscribers

People subscribed via source and target branches

to status/vote changes: