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
1=== modified file 'environs/local/config.go'
2--- environs/local/config.go 2013-07-07 22:34:06 +0000
3+++ environs/local/config.go 2013-07-16 21:59:25 +0000
4@@ -13,6 +13,10 @@
5 "launchpad.net/juju-core/schema"
6 )
7
8+var checkIfRoot = func() bool {
9+ return os.Getuid() == 0
10+}
11+
12 var configChecker = schema.StrictFieldMap(
13 schema.Fields{
14 "root-dir": schema.String(),
15@@ -31,9 +35,8 @@
16
17 func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig {
18 user := os.Getenv("USER")
19- root := false
20- if user == "root" {
21- root = true
22+ root := checkIfRoot()
23+ if root {
24 sudo_user := os.Getenv("SUDO_USER")
25 if sudo_user != "" {
26 user = sudo_user
27@@ -74,6 +77,28 @@
28 return filepath.Join(c.rootDir(), filename)
29 }
30
31+// sudoCallerIds returns the user id and group id of the SUDO caller.
32+// If either is unset, it returns zero for both values.
33+// An error is returned if the relevant environment variables
34+// are not valid integers.
35+func sudoCallerIds() (int, int, error) {
36+ uidStr := os.Getenv("SUDO_UID")
37+ gidStr := os.Getenv("SUDO_GID")
38+
39+ if uidStr == "" || gidStr == "" {
40+ return 0, 0, nil
41+ }
42+ uid, err := strconv.Atoi(uidStr)
43+ if err != nil {
44+ return 0, 0, fmt.Errorf("invalid value %q for SUDO_UID", uidStr)
45+ }
46+ gid, err := strconv.Atoi(gidStr)
47+ if err != nil {
48+ return 0, 0, fmt.Errorf("invalid value %q for SUDO_GID", gidStr)
49+ }
50+ return uid, gid, nil
51+}
52+
53 func (c *environConfig) createDirs() error {
54 for _, dirname := range []string{
55 c.sharedStorageDir(),
56@@ -88,23 +113,14 @@
57 if c.runningAsRoot {
58 // If we have SUDO_UID and SUDO_GID, start with rootDir(), and
59 // change ownership of the directories.
60- uidStr := os.Getenv("SUDO_UID")
61- gidStr := os.Getenv("SUDO_GID")
62- if uidStr != "" && gidStr != "" {
63- uid, err := strconv.Atoi(uidStr)
64- if err != nil {
65- logger.Errorf("Expected %q for SUDO_UID to be an int: %v", uidStr, err)
66- return err
67- }
68- gid, err := strconv.Atoi(gidStr)
69- if err != nil {
70- logger.Errorf("Expected %q for SUDO_GID to be an int: %v", gidStr, err)
71- return err
72- }
73-
74+ uid, gid, err := sudoCallerIds()
75+ if err != nil {
76+ return err
77+ }
78+ if uid != 0 || gid != 0 {
79 filepath.Walk(c.rootDir(),
80 func(path string, info os.FileInfo, err error) error {
81- if info.IsDir() && err == nil {
82+ if info != nil && info.IsDir() {
83 if err := os.Chown(path, uid, gid); err != nil {
84 return err
85 }
86
87=== modified file 'environs/local/config_test.go'
88--- environs/local/config_test.go 2013-07-15 02:58:47 +0000
89+++ environs/local/config_test.go 2013-07-16 21:59:25 +0000
90@@ -83,6 +83,8 @@
91 }
92
93 func (s *configSuite) TestNamespaceRootNoSudo(c *gc.C) {
94+ restore := local.SetRootCheckFunction(func() bool { return true })
95+ defer restore()
96 err := os.Setenv("USER", "root")
97 c.Assert(err, gc.IsNil)
98 testConfig := minimalConfig(c)
99@@ -90,6 +92,8 @@
100 }
101
102 func (s *configSuite) TestNamespaceRootWithSudo(c *gc.C) {
103+ restore := local.SetRootCheckFunction(func() bool { return true })
104+ defer restore()
105 err := os.Setenv("USER", "root")
106 c.Assert(err, gc.IsNil)
107 err = os.Setenv("SUDO_USER", "tester")
108@@ -99,6 +103,47 @@
109 c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test")
110 }
111
112+func (s *configSuite) TestSudoCallerIds(c *gc.C) {
113+ defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
114+ defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))
115+ for _, test := range []struct {
116+ uid string
117+ gid string
118+ errString string
119+ expectedUid int
120+ expectedGid int
121+ }{{
122+ uid: "",
123+ gid: "",
124+ }, {
125+ uid: "1001",
126+ gid: "1002",
127+ expectedUid: 1001,
128+ expectedGid: 1002,
129+ }, {
130+ uid: "1001",
131+ gid: "foo",
132+ errString: `invalid value "foo" for SUDO_GID`,
133+ }, {
134+ uid: "foo",
135+ gid: "bar",
136+ errString: `invalid value "foo" for SUDO_UID`,
137+ }} {
138+ os.Setenv("SUDO_UID", test.uid)
139+ os.Setenv("SUDO_GID", test.gid)
140+ uid, gid, err := local.SudoCallerIds()
141+ if test.errString == "" {
142+ c.Assert(err, gc.IsNil)
143+ c.Assert(uid, gc.Equals, test.expectedUid)
144+ c.Assert(gid, gc.Equals, test.expectedGid)
145+ } else {
146+ c.Assert(err, gc.ErrorMatches, test.errString)
147+ c.Assert(uid, gc.Equals, 0)
148+ c.Assert(gid, gc.Equals, 0)
149+ }
150+ }
151+}
152+
153 type configRootSuite struct {
154 baseProviderSuite
155 }
156
157=== modified file 'environs/local/export_test.go'
158--- environs/local/export_test.go 2013-07-15 02:58:47 +0000
159+++ environs/local/export_test.go 2013-07-16 21:59:25 +0000
160@@ -10,6 +10,14 @@
161
162 var Provider = provider
163
164+// SetRootCheckFunction allows tests to override the check for a root user.
165+// The return value is the function to restore the old value.
166+func SetRootCheckFunction(f func() bool) func() {
167+ old := checkIfRoot
168+ checkIfRoot = f
169+ return func() { checkIfRoot = old }
170+}
171+
172 // ConfigNamespace returns the result of the namespace call on the
173 // localConfig.
174 func ConfigNamespace(cfg *config.Config) string {
175@@ -35,3 +43,7 @@
176 localConfig.mongoDir(),
177 }
178 }
179+
180+func SudoCallerIds() (uid, gid int, err error) {
181+ return sudoCallerIds()
182+}

Subscribers

People subscribed via source and target branches

to status/vote changes: