many: abstract path to /bin/{true,false} #3096

Merged
merged 12 commits into from Apr 5, 2017

Conversation

Projects
None yet
4 participants
Contributor

morphis commented Mar 28, 2017

The true and false command line utilities are not on all systems in /bin. Therefor this PR implements a tiny helper method which searches for commands in PATH and returns the found path. This allows us now to abstract the path to relevant binaries instead of specifying ones specific for a single OS. On Fedora and openSUSE true and false are in /usr/bin.

@zyga zyga self-requested a review Mar 28, 2017

Some comments. +1 on most but worried that setenv is waiting to bite us.

osutil/find_in_path_test.go
+}
+
+func (s *findInPathSuite) SetUpSuite(c *C) {
+ p, err := ioutil.TempDir("", "find-in-path")
@zyga

zyga Mar 31, 2017

Contributor

Maybe just c.MkDir for a temporary directory you don't have to manage?

@morphis

morphis Mar 31, 2017

Contributor

Isn't that what ioutil.TempDir does internally for me with the advantage of having a unique dir for every test run?

@zyga

zyga Mar 31, 2017

Contributor

Yep, it's just more integrated as I think it gets removed automatically.

osutil/find_in_path_test.go
+ c.Assert(err, Equals, nil)
+ s.basePath = p
+ s.envPath = os.Getenv("PATH")
+ os.Setenv("PATH", fmt.Sprintf("%s/bin:%s/usr/bin", s.basePath, s.basePath))
@zyga

zyga Mar 31, 2017

Contributor

Os.Setenv is problematic. Not sure if we should call it or should just mock something like osPath instead.

https://rachelbythebay.com/w/2017/01/30/env/

I'm not sure if golang calls the C library or just buffers the operation.

@morphis

morphis Mar 31, 2017

Contributor

Fixed.

osutil/find_in_path_test.go
+ }
+}
+
+func (s *findInPathSuite) TearDownSuite(c *C) {
@zyga

zyga Mar 31, 2017

Contributor

I'd rather mock the place that gives us getenv

@morphis

morphis Mar 31, 2017

Contributor

Agreed. Let me rework that.

@morphis

morphis Mar 31, 2017

Contributor

Fixed.

Simon Fels added some commits Mar 28, 2017

zyga approved these changes Mar 31, 2017

Looks good to me now, thanks!

Some comments (sorry that its slightly verbose)

errtracker/errtracker_test.go
@@ -49,23 +49,31 @@ type ErrtrackerTestSuite struct {
var _ = Suite(&ErrtrackerTestSuite{})
+var truePath string
@mvo5

mvo5 Apr 3, 2017

Collaborator

You can write this as:

var truePath = osutil.FindInPathOrDefault("true", "/bin/true")
var falsePath = osutil.FindInPathOrDefault("false", "/bin/false")

to safe some lines of code :) (i.e. no need for the extra init() AFAICS).

@morphis

morphis Apr 3, 2017

Contributor

Fixed

osutil/common_test.go
+var gccPath string
+
+func init() {
+ truePath = osutil.FindInPathOrDefault("true", "/bin/true")
@mvo5

mvo5 Apr 3, 2017

Collaborator

Same as above, this can go directly to the "var" bits to safe some lines.

@morphis

morphis Apr 3, 2017

Contributor

Fixed

osutil/find_in_path.go
+// listed in the environment variable PATH and returns the found path or the
+// provided default path.
+func FindInPathOrDefault(name string, defaultPath string) string {
+ paths := strings.Split(Getenv("PATH"), ":")
@mvo5

mvo5 Apr 3, 2017

Collaborator

There is a exec.LookupPath() which is doing what this function is doing except the "default" part. So maybe use that and when LookupPath returns an ErrNotFound return the provided default?

@morphis

morphis Apr 3, 2017

Contributor

Nice one. Integrated it into the code now.

osutil/find_in_path.go
+func FindInPathOrDefault(name string, defaultPath string) string {
+ paths := strings.Split(Getenv("PATH"), ":")
+ for _, p := range paths {
+ candidate := fmt.Sprintf("%s/%s", p, name)
@mvo5

mvo5 Apr 3, 2017

Collaborator

I slightly prefer candidate := filepath.Join(p, name) to join the dir and the filename (but its to a certain extend nitpick :)

osutil/find_in_path.go
+ paths := strings.Split(Getenv("PATH"), ":")
+ for _, p := range paths {
+ candidate := fmt.Sprintf("%s/%s", p, name)
+ _, err := os.Stat(candidate)
@mvo5

mvo5 Apr 3, 2017

Collaborator

You could use if FileExists(candidate) { return candidate } here which is slightly shorter.

Simon Fels added some commits Apr 3, 2017

osutil/find_in_path.go
@@ -38,13 +36,9 @@ func FindInPath(name string) string {
// listed in the environment variable PATH and returns the found path or the
// provided default path.
func FindInPathOrDefault(name string, defaultPath string) string {
@mvo5

mvo5 Apr 3, 2017

Collaborator

Sorry for being complicated, I wonder if maybe LookupPathWithDefault() so that its closer to the exec.LookupPath (i.e. less cognitive load by having "FindInPath" vs "LookupPath" depending on what is needed).

@morphis

morphis Apr 4, 2017

Contributor

Done.

mvo5 approved these changes Apr 3, 2017

Looks good, thanks for doing this work! One final nitpick but feel free to ignore (or merge if you want as is).

mvo5 approved these changes Apr 4, 2017

zyga approved these changes Apr 4, 2017

LGTM

@mvo5 mvo5 added this to the 2.24 milestone Apr 4, 2017

zyga added some commits Apr 4, 2017

A few changes requested to make the amount of changes more proportional to the problem being solved. Please feel free to merge when you're happy about the handling of these problems.

osutil/common_test.go
+
+var truePath = osutil.LookupPathWithDefault("true", "/bin/true")
+var falsePath = osutil.LookupPathWithDefault("false", "/bin/false")
+var gccPath = osutil.LookupPathWithDefault("gcc", "/usr/bin/gcc")
@niemeyer

niemeyer Apr 4, 2017

Contributor

These should be close to where they are used instead of in a file by themselves. If they are used in more than one file, it's okay to pick the one which uses them the most, or the one you like the most. :)

@morphis

morphis Apr 5, 2017

Contributor

Done

osutil/lookup_path.go
+// LookupPathWithDefault searches for a given command name in all directories
+// listed in the environment variable PATH and returns the found path or the
+// provided default path.
+func LookupPathWithDefault(name string, defaultPath string) string {
@niemeyer

niemeyer Apr 4, 2017

Contributor

This feels a little like overshooting a bit given the real needs we have in this PR, as this is the only function we really needed, and their names don't actually give any hint about why we have the three (LookupPath vs. LookPath).

Instead, I suggest adding just the following implementation with this name:

func LookPathDefault(name, defpath string) string {

If we need LookPath itself, we can use the exec package directly, with the familiar name and signature.

In tests, it should be easy to manipulate the environment to test the real function without using mocks.

@morphis

morphis Apr 5, 2017

Contributor

@niemeyer I was asked by @zyga to not use os.Setenv in the unit tests. Changing the code to just have LookPathDefault

@morphis

morphis Apr 5, 2017

Contributor

Done

osutil/lookup_path_test.go
+
+var _ = Suite(&lookupPathSuite{})
+
+func (s *lookupPathSuite) TestGivesCorrectPath(c *C) {
@niemeyer

niemeyer Apr 4, 2017

Contributor

Similarly, let's integrate these into stat_test.go.

For the record, this package is overbroken, so I apologize for the bad example. We should eventually merge some of those tiny one-function files into proper chunks by similarity. A bit like stat.go.

@morphis

morphis Apr 5, 2017

Contributor

Done

Simon Fels added some commits Apr 5, 2017

Looks great, thank you!

@mvo5 mvo5 merged commit 304acf5 into snapcore:master Apr 5, 2017

6 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
xenial-amd64 autopkgtest finished (success)
Details
xenial-i386 autopkgtest finished (success)
Details
xenial-ppc64el autopkgtest finished (success)
Details
yakkety-amd64 autopkgtest finished (success)
Details
zesty-amd64 autopkgtest finished (success)
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment