Merge lp:~thumper/juju-core/local-sudo-caller into lp:~go-bot/juju-core/trunk
- local-sudo-caller
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email:
|
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.
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Penhey (thumper) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ian Booth (wallyworld) wrote : | # |
LGTM
https:/
File environs/
https:/
environs/
Perhaps checkRoot() is better?
https:/
environs/
I don't think the above comment belongs here
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John A Meinel (jameinel) wrote : | # |
LGTM
https:/
File environs/
https:/
environs/
On 2013/07/16 03:56:35, wallyworld wrote:
> Perhaps checkRoot() is better?
checkIfRoot ?
https:/
File environs/
https:/
environs/
local.SetRootCh
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 SetRootCheckFun
https:/
File environs/
https:/
environs/
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Roger Peppe (rogpeppe) wrote : | # |
LGTM with a few minor suggestions and general support of john's remarks,
thought i don't mind much.
https:/
File environs/
https:/
environs/
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:/
environs/
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:/
environs/
error) {
sudoCallerIds ?
https:/
environs/
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:/
environs/
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(
}
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:/
environs/
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Frank Mueller (themue) wrote : | # |
https:/
File environs/
https:/
environs/
Any need why this is a variable?
https:/
environs/
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(
> }
> 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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Penhey (thumper) wrote : | # |
https:/
File environs/
https:/
environs/
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:/
environs/
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:/
environs/
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:/
environs/
error) {
On 2013/07/16 13:30:01, rog wrote:
> sudoCallerIds ?
You really don't like "get" do you?
Renamed.
https:/
environs/
On 2013/07/16 03:56:35, wallyworld wrote:
> I don't think the above comment belongs here
No it isn't. Deleted.
https:/
environs/
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:/
environs/
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(
> > }
> > 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:/
environs/
On 2013/07/16 13:30:01, rog wrote:
> if err == nil && info.IsDir() {
> (if there's an error,...
Preview Diff
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 | 13 | "launchpad.net/juju-core/schema" | 13 | "launchpad.net/juju-core/schema" |
6 | 14 | ) | 14 | ) |
7 | 15 | 15 | ||
8 | 16 | var checkIfRoot = func() bool { | ||
9 | 17 | return os.Getuid() == 0 | ||
10 | 18 | } | ||
11 | 19 | |||
12 | 16 | var configChecker = schema.StrictFieldMap( | 20 | var configChecker = schema.StrictFieldMap( |
13 | 17 | schema.Fields{ | 21 | schema.Fields{ |
14 | 18 | "root-dir": schema.String(), | 22 | "root-dir": schema.String(), |
15 | @@ -31,9 +35,8 @@ | |||
16 | 31 | 35 | ||
17 | 32 | func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig { | 36 | func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig { |
18 | 33 | user := os.Getenv("USER") | 37 | user := os.Getenv("USER") |
22 | 34 | root := false | 38 | root := checkIfRoot() |
23 | 35 | if user == "root" { | 39 | if root { |
21 | 36 | root = true | ||
24 | 37 | sudo_user := os.Getenv("SUDO_USER") | 40 | sudo_user := os.Getenv("SUDO_USER") |
25 | 38 | if sudo_user != "" { | 41 | if sudo_user != "" { |
26 | 39 | user = sudo_user | 42 | user = sudo_user |
27 | @@ -74,6 +77,28 @@ | |||
28 | 74 | return filepath.Join(c.rootDir(), filename) | 77 | return filepath.Join(c.rootDir(), filename) |
29 | 75 | } | 78 | } |
30 | 76 | 79 | ||
31 | 80 | // sudoCallerIds returns the user id and group id of the SUDO caller. | ||
32 | 81 | // If either is unset, it returns zero for both values. | ||
33 | 82 | // An error is returned if the relevant environment variables | ||
34 | 83 | // are not valid integers. | ||
35 | 84 | func sudoCallerIds() (int, int, error) { | ||
36 | 85 | uidStr := os.Getenv("SUDO_UID") | ||
37 | 86 | gidStr := os.Getenv("SUDO_GID") | ||
38 | 87 | |||
39 | 88 | if uidStr == "" || gidStr == "" { | ||
40 | 89 | return 0, 0, nil | ||
41 | 90 | } | ||
42 | 91 | uid, err := strconv.Atoi(uidStr) | ||
43 | 92 | if err != nil { | ||
44 | 93 | return 0, 0, fmt.Errorf("invalid value %q for SUDO_UID", uidStr) | ||
45 | 94 | } | ||
46 | 95 | gid, err := strconv.Atoi(gidStr) | ||
47 | 96 | if err != nil { | ||
48 | 97 | return 0, 0, fmt.Errorf("invalid value %q for SUDO_GID", gidStr) | ||
49 | 98 | } | ||
50 | 99 | return uid, gid, nil | ||
51 | 100 | } | ||
52 | 101 | |||
53 | 77 | func (c *environConfig) createDirs() error { | 102 | func (c *environConfig) createDirs() error { |
54 | 78 | for _, dirname := range []string{ | 103 | for _, dirname := range []string{ |
55 | 79 | c.sharedStorageDir(), | 104 | c.sharedStorageDir(), |
56 | @@ -88,23 +113,14 @@ | |||
57 | 88 | if c.runningAsRoot { | 113 | if c.runningAsRoot { |
58 | 89 | // If we have SUDO_UID and SUDO_GID, start with rootDir(), and | 114 | // If we have SUDO_UID and SUDO_GID, start with rootDir(), and |
59 | 90 | // change ownership of the directories. | 115 | // change ownership of the directories. |
74 | 91 | uidStr := os.Getenv("SUDO_UID") | 116 | uid, gid, err := sudoCallerIds() |
75 | 92 | gidStr := os.Getenv("SUDO_GID") | 117 | if err != nil { |
76 | 93 | if uidStr != "" && gidStr != "" { | 118 | return err |
77 | 94 | uid, err := strconv.Atoi(uidStr) | 119 | } |
78 | 95 | if err != nil { | 120 | if uid != 0 || gid != 0 { |
65 | 96 | logger.Errorf("Expected %q for SUDO_UID to be an int: %v", uidStr, err) | ||
66 | 97 | return err | ||
67 | 98 | } | ||
68 | 99 | gid, err := strconv.Atoi(gidStr) | ||
69 | 100 | if err != nil { | ||
70 | 101 | logger.Errorf("Expected %q for SUDO_GID to be an int: %v", gidStr, err) | ||
71 | 102 | return err | ||
72 | 103 | } | ||
73 | 104 | |||
79 | 105 | filepath.Walk(c.rootDir(), | 121 | filepath.Walk(c.rootDir(), |
80 | 106 | func(path string, info os.FileInfo, err error) error { | 122 | func(path string, info os.FileInfo, err error) error { |
82 | 107 | if info.IsDir() && err == nil { | 123 | if info != nil && info.IsDir() { |
83 | 108 | if err := os.Chown(path, uid, gid); err != nil { | 124 | if err := os.Chown(path, uid, gid); err != nil { |
84 | 109 | return err | 125 | return err |
85 | 110 | } | 126 | } |
86 | 111 | 127 | ||
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 | 83 | } | 83 | } |
92 | 84 | 84 | ||
93 | 85 | func (s *configSuite) TestNamespaceRootNoSudo(c *gc.C) { | 85 | func (s *configSuite) TestNamespaceRootNoSudo(c *gc.C) { |
94 | 86 | restore := local.SetRootCheckFunction(func() bool { return true }) | ||
95 | 87 | defer restore() | ||
96 | 86 | err := os.Setenv("USER", "root") | 88 | err := os.Setenv("USER", "root") |
97 | 87 | c.Assert(err, gc.IsNil) | 89 | c.Assert(err, gc.IsNil) |
98 | 88 | testConfig := minimalConfig(c) | 90 | testConfig := minimalConfig(c) |
99 | @@ -90,6 +92,8 @@ | |||
100 | 90 | } | 92 | } |
101 | 91 | 93 | ||
102 | 92 | func (s *configSuite) TestNamespaceRootWithSudo(c *gc.C) { | 94 | func (s *configSuite) TestNamespaceRootWithSudo(c *gc.C) { |
103 | 95 | restore := local.SetRootCheckFunction(func() bool { return true }) | ||
104 | 96 | defer restore() | ||
105 | 93 | err := os.Setenv("USER", "root") | 97 | err := os.Setenv("USER", "root") |
106 | 94 | c.Assert(err, gc.IsNil) | 98 | c.Assert(err, gc.IsNil) |
107 | 95 | err = os.Setenv("SUDO_USER", "tester") | 99 | err = os.Setenv("SUDO_USER", "tester") |
108 | @@ -99,6 +103,47 @@ | |||
109 | 99 | c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test") | 103 | c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test") |
110 | 100 | } | 104 | } |
111 | 101 | 105 | ||
112 | 106 | func (s *configSuite) TestSudoCallerIds(c *gc.C) { | ||
113 | 107 | defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID")) | ||
114 | 108 | defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID")) | ||
115 | 109 | for _, test := range []struct { | ||
116 | 110 | uid string | ||
117 | 111 | gid string | ||
118 | 112 | errString string | ||
119 | 113 | expectedUid int | ||
120 | 114 | expectedGid int | ||
121 | 115 | }{{ | ||
122 | 116 | uid: "", | ||
123 | 117 | gid: "", | ||
124 | 118 | }, { | ||
125 | 119 | uid: "1001", | ||
126 | 120 | gid: "1002", | ||
127 | 121 | expectedUid: 1001, | ||
128 | 122 | expectedGid: 1002, | ||
129 | 123 | }, { | ||
130 | 124 | uid: "1001", | ||
131 | 125 | gid: "foo", | ||
132 | 126 | errString: `invalid value "foo" for SUDO_GID`, | ||
133 | 127 | }, { | ||
134 | 128 | uid: "foo", | ||
135 | 129 | gid: "bar", | ||
136 | 130 | errString: `invalid value "foo" for SUDO_UID`, | ||
137 | 131 | }} { | ||
138 | 132 | os.Setenv("SUDO_UID", test.uid) | ||
139 | 133 | os.Setenv("SUDO_GID", test.gid) | ||
140 | 134 | uid, gid, err := local.SudoCallerIds() | ||
141 | 135 | if test.errString == "" { | ||
142 | 136 | c.Assert(err, gc.IsNil) | ||
143 | 137 | c.Assert(uid, gc.Equals, test.expectedUid) | ||
144 | 138 | c.Assert(gid, gc.Equals, test.expectedGid) | ||
145 | 139 | } else { | ||
146 | 140 | c.Assert(err, gc.ErrorMatches, test.errString) | ||
147 | 141 | c.Assert(uid, gc.Equals, 0) | ||
148 | 142 | c.Assert(gid, gc.Equals, 0) | ||
149 | 143 | } | ||
150 | 144 | } | ||
151 | 145 | } | ||
152 | 146 | |||
153 | 102 | type configRootSuite struct { | 147 | type configRootSuite struct { |
154 | 103 | baseProviderSuite | 148 | baseProviderSuite |
155 | 104 | } | 149 | } |
156 | 105 | 150 | ||
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 | 10 | 10 | ||
162 | 11 | var Provider = provider | 11 | var Provider = provider |
163 | 12 | 12 | ||
164 | 13 | // SetRootCheckFunction allows tests to override the check for a root user. | ||
165 | 14 | // The return value is the function to restore the old value. | ||
166 | 15 | func SetRootCheckFunction(f func() bool) func() { | ||
167 | 16 | old := checkIfRoot | ||
168 | 17 | checkIfRoot = f | ||
169 | 18 | return func() { checkIfRoot = old } | ||
170 | 19 | } | ||
171 | 20 | |||
172 | 13 | // ConfigNamespace returns the result of the namespace call on the | 21 | // ConfigNamespace returns the result of the namespace call on the |
173 | 14 | // localConfig. | 22 | // localConfig. |
174 | 15 | func ConfigNamespace(cfg *config.Config) string { | 23 | func ConfigNamespace(cfg *config.Config) string { |
175 | @@ -35,3 +43,7 @@ | |||
176 | 35 | localConfig.mongoDir(), | 43 | localConfig.mongoDir(), |
177 | 36 | } | 44 | } |
178 | 37 | } | 45 | } |
179 | 46 | |||
180 | 47 | func SudoCallerIds() (uid, gid int, err error) { | ||
181 | 48 | return sudoCallerIds() | ||
182 | 49 | } |
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: local/config. go local/config_ test.go local/export_ test.go
A [revision details]
M environs/
M environs/
M environs/