-
Notifications
You must be signed in to change notification settings - Fork 881
Implement composable uid/gid generators #2510
Conversation
a57ed26 to
649084a
Compare
|
I just recognized ... now that I factored that jazz out I really should introduce unit tests ;-) |
pkg/uid/from_etc.go
Outdated
|
|
||
| // FromEtc is the struct that generates a uid/gid by parsing etc/passwd, | ||
| // and etc/group relative from the given Root path looking for the given Name. | ||
| type FromEtc struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You missed a NewFromEtc for this one.
Further, since all of your constructors return the interface, there's no need to export any of these structs - and indeed, to me it's preferable not to, since it minimises the exposed interface to the package. However, obviously if you don't export them, you will need to test them within the same package (hi @tmrts).
And personally I think all the generators should be in a single file - it's a relatively trivial amount of code, and now uid_range.go is an odd exception in the package.
buuut - I want to leave the bulk of this review to @tmrts ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed NewFromEtc to make the invariant of a non-nil Etc based uid/gid generator explicit but I see this confuses more than it helps, I'll introduce it then.
Why not exposing the structs? Just because they implement the interface doesn't mean they are unusable without that fact. But I have no strong opinion on that so I'll gladly make it all private.
Let's see @tmrts's opinion :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonboulle I now have a better idea of your position in "_test". Here is how everything should be tested without exporting everything:
package uid
type Generator interface{
....
}
type fromEtc struct {...}
var _ Generator = &fromEtc{}
func FromEtc(....) Generator {
...
return &fromEtc{...}
}and the tests should be like:
package uid_test
func TestCreatesIDGeneratorFromEtc(t *testing.T) {
g := uid.FromEtc(...)
if g.UID() != expected {
...
}
if g.GID() != expected {
...
}and this function should be repeated for all exported constructors or a table-driven test should be used.
This way you test everything and you only export the interface and constructors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@s-urbaniak if you can avoid exported structs, it's much better to export interfaces and let the clients depend on interfaces/behavior. This way we can easily test a function that uses a generator from this package by mocking the interface. Otherwise you would have to create mocks for each exported struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with all that have been said above, and the existing implementation did not contradict with mocking though since all New.. functions already return the Generator interface. Nevertheless I'll make all of them private and in one source file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way you test everything and you only export the interface and constructors.
It actually works in this particular case, but only because i) the implementation is particularly simple, so you can be fairly confident that all code paths are covered; and ii) the interface happens to expose a path override, which is ideal for testing. But such customisations are not always desirable in interfaces and it's unclean to add them to external interfaces only for testing purposes. It's quite plausible, for example, that the package could instead expose NewFromEtcdPasswd() Generator, pinned to using /etc/passwd, in which case you would internally have a way to customise and test with an alternative path (newFromSpecificFile), but never expose this to package consumers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, how do you plan to do any code coverage? Last time I checked go wasn't able to do it cross-package.
pkg/uid/from_etc.go
Outdated
| // For how the uidshift and uidcount are generated please check: | ||
| // http://cgit.freedesktop.org/systemd/systemd/commit/?id=03cfe0d51499e86b1573d1 | ||
|
|
||
| package uid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uid is not a very descriptive name since it is about extracting uid & gid.
A better name would be nss or nssid since passwd, shadow, etc. are all members of Name Service Switch name resolution mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nss is an implementation detail, as suggested below I'd call this user.
|
After the modifications I commented plus the tests, LGTM |
stage1/init/common/pod.go
Outdated
| ) | ||
|
|
||
| if uid_, err = uidGen.UID(); err != nil { | ||
| if uid_, _, err = uidGen.IDs(); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uid_ could be now renamed to uid. same with gid_ below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good catch, thanks!
3385934 to
0483b1b
Compare
|
blocked by #2515 for providing functional tests for fly, this is too short notice, rescheduling for the next release. |
0483b1b to
07ddf8b
Compare
pkg/user/generator.go
Outdated
| if gid, err = group.LookupGidFromFile( | ||
| e.group, | ||
| filepath.Join(e.rootPath, "etc/group"), | ||
| ); e.group != "" && err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't use multiple line statements in if initializations.
gid, err = .....
if e.group != "" && err != nil {
...
}
returnwould be much better since you are using named return values.
fb32b3b to
645156b
Compare
|
TODO: backport fix from #2508 (done) |
645156b to
1568129
Compare
94316e9 to
a601390
Compare
pkg/user/generator.go
Outdated
| return idsFromStat{filepath.Join(rootPath, file), r} | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't do this please. User has to start checking for nil values, which is tedious and error-prone and if it receives a nil he won't know the reason, worse it won't be traceable without a proper error.
Instead modify the signature to (Generator, error) from Generator
Same goes for other constructors.
PickGenerator is used to get a valid generator by checking nil, but you can also check the errors and log them and pick a working one. I want to avoid introducing nil checks unless it's an absolute must.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, what's your suggestion to set up a generator chain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'll leave chaining up to the user
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd agree with leaving it up to user unless you have a specific case in mind.
a601390 to
2e68415
Compare
f118817 to
d3c0c9e
Compare
|
LGTM |
|
Do you wanna squash the commits? |
Currently resolving UIDs/GIDs is implemented in a single private method in stage1/init/common/pod.go. For future reuse/compositionality this is not the best solution. This refactors resolving UIDs/GIDs in a new user package. It introduces uid/gid resolvers capable of resolving those values from stat()ing a file, looking up etc/passwd, or etc/group, or simply assigning static uid/gid values. Fixes rkt#2439 Backports rkt#2508
d3c0c9e to
748c626
Compare
|
@iaguis squashed |
|
I didn't go very deep reviewing this but LGTM on the surface. |
This cleans up the code a bit and provides the necessary uid/gid functionality for implementing user/groups for rkt fly, see #2439