Merge lp:~thumper/juju-core/local-ensure-root 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: 2452
Proposed branch: lp:~thumper/juju-core/local-ensure-root
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 136 lines (+81/-4)
3 files modified
cmd/plugins/local/export_test.go (+8/-1)
cmd/plugins/local/main.go (+34/-0)
cmd/plugins/local/main_test.go (+39/-3)
To merge this branch: bzr merge lp:~thumper/juju-core/local-ensure-root
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+211849@code.launchpad.net

Commit message

Implement a helper function for sudo.

Most of the local plugin helper commands will need
to run as sudo. Instead of each writing their own
code to handle this, provide a helper function.

https://codereview.appspot.com/78030044/

Description of the change

Implement a helper function for sudo.

Most of the local plugin helper commands will need
to run as sudo. Instead of each writing their own
code to handle this, provide a helper function.

https://codereview.appspot.com/78030044/

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

Reviewers: mp+211849_code.launchpad.net,

Message:
Please take a look.

Description:
Implement a helper function for sudo.

Most of the local plugin helper commands will need
to run as sudo. Instead of each writing their own
code to handle this, provide a helper function.

https://code.launchpad.net/~thumper/juju-core/local-ensure-root/+merge/211849

(do not edit description out of merge proposal)

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

Affected files (+79, -4 lines):
   A [revision details]
   M cmd/plugins/local/export_test.go
   M cmd/plugins/local/main.go
   M cmd/plugins/local/main_test.go

Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: tarmac-20140320011142-7ljymhlzvpq44apl
+New revision: <email address hidden>

Index: cmd/plugins/local/export_test.go
=== modified file 'cmd/plugins/local/export_test.go'
--- cmd/plugins/local/export_test.go 2014-02-27 05:16:54 +0000
+++ cmd/plugins/local/export_test.go 2014-03-19 21:53:07 +0000
@@ -3,4 +3,11 @@

  package local

-var JujuLocalPlugin = jujuLocalPlugin
+var (
+ // Variable is the address so we can PatchValue
+ CheckIfRoot = &checkIfRoot
+
+ // function exports for tests
+ EnsureRoot = ensureRoot
+ JujuLocalPlugin = jujuLocalPlugin
+)

Index: cmd/plugins/local/main.go
=== modified file 'cmd/plugins/local/main.go'
--- cmd/plugins/local/main.go 2014-03-05 19:41:34 +0000
+++ cmd/plugins/local/main.go 2014-03-19 22:29:47 +0000
@@ -5,6 +5,7 @@

  import (
   "os"
+ "os/exec"

   "github.com/juju/loggo"

@@ -39,3 +40,32 @@
   plugin := jujuLocalPlugin()
   os.Exit(cmd.Main(plugin, cmd.DefaultContext(), args[1:]))
  }
+
+var checkIfRoot = func() bool {
+ return os.Getuid() == 0
+}
+
+func ensureRoot(args []string, context *cmd.Context, call
func(*cmd.Context) error) error {
+ if checkIfRoot() {
+ logger.Debugf("running as root")
+ return call(context)
+ }
+
+ logger.Debugf("running as user")
+
+ fullpath, err := exec.LookPath(args[0])
+ if err != nil {
+ return err
+ }
+
+ sudoArgs := []string{"--preserve-env", fullpath}
+ sudoArgs = append(sudoArgs, args[1:]...)
+
+ command := exec.Command("sudo", sudoArgs...)
+ // Now hook up stdin, stdout, stderr
+ command.Stdin = context.Stdin
+ command.Stdout = context.Stdout
+ command.Stderr = context.Stderr
+ // And run it!
+ return command.Run()
+}

Index: cmd/plugins/local/main_test.go
=== modified file 'cmd/plugins/local/main_test.go'
--- cmd/plugins/local/main_test.go 2014-02-27 05:16:54 +0000
+++ cmd/plugins/local/main_test.go 2014-03-20 01:58:59 +0000
@@ -4,12 +4,17 @@
  package local_test

  import (
+ "fmt"
+ "os/exec"
   "strings"

+ "github.com/juju/testing"
+ jc "github.com/juju/testing/checkers"
   gc "launchpad.net/gocheck"

+ "launchpad.net/juju-core/cmd"
   "launchpad.net/juju-core/cmd/plugins/local"
- "launchpad.net/juju-core/testing"
+ coretesting "launchpad.net/juju-core/testing"
   "launchpad.net/juju-core/testing/testbase"
  )

@@ -25,10 +30,10 @@
    // TODO: add some as they get registered
   }
   plugin := local.JujuLocal...

Read more...

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

LGTM with suggestion considered

https://codereview.appspot.com/78030044/diff/1/cmd/plugins/local/main.go
File cmd/plugins/local/main.go (right):

https://codereview.appspot.com/78030044/diff/1/cmd/plugins/local/main.go#newcode48
cmd/plugins/local/main.go:48: func ensureRoot(args []string, context
*cmd.Context, call func(*cmd.Context) error) error {
Even though not exported, a doc comment would be nice. Maybe include
that args contains cmd to run in args[0] etc. I think I'd prefer args
split up so that command is passed in explicitly

func ensureRoot(cmd string, cmdArgs []string, context *cmd.Context, call
func(*cmd.Context) error {
}

And maybe call the method runAsRoot()

https://codereview.appspot.com/78030044/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/plugins/local/export_test.go'
2--- cmd/plugins/local/export_test.go 2014-02-27 05:16:54 +0000
3+++ cmd/plugins/local/export_test.go 2014-03-20 04:14:57 +0000
4@@ -3,4 +3,11 @@
5
6 package local
7
8-var JujuLocalPlugin = jujuLocalPlugin
9+var (
10+ // Variable is the address so we can PatchValue
11+ CheckIfRoot = &checkIfRoot
12+
13+ // function exports for tests
14+ RunAsRoot = runAsRoot
15+ JujuLocalPlugin = jujuLocalPlugin
16+)
17
18=== modified file 'cmd/plugins/local/main.go'
19--- cmd/plugins/local/main.go 2014-03-05 19:41:34 +0000
20+++ cmd/plugins/local/main.go 2014-03-20 04:14:57 +0000
21@@ -5,6 +5,7 @@
22
23 import (
24 "os"
25+ "os/exec"
26
27 "github.com/juju/loggo"
28
29@@ -39,3 +40,36 @@
30 plugin := jujuLocalPlugin()
31 os.Exit(cmd.Main(plugin, cmd.DefaultContext(), args[1:]))
32 }
33+
34+var checkIfRoot = func() bool {
35+ return os.Getuid() == 0
36+}
37+
38+// runAsRoot ensures that the executable is running as root.
39+// If checkIfRoot returns true, the call function is called,
40+// otherwise executable is executed using sudo and the extra args
41+// passed through.
42+func runAsRoot(executable string, args []string, context *cmd.Context, call func(*cmd.Context) error) error {
43+ if checkIfRoot() {
44+ logger.Debugf("running as root")
45+ return call(context)
46+ }
47+
48+ logger.Debugf("running as user")
49+
50+ fullpath, err := exec.LookPath(executable)
51+ if err != nil {
52+ return err
53+ }
54+
55+ sudoArgs := []string{"--preserve-env", fullpath}
56+ sudoArgs = append(sudoArgs, args...)
57+
58+ command := exec.Command("sudo", sudoArgs...)
59+ // Now hook up stdin, stdout, stderr
60+ command.Stdin = context.Stdin
61+ command.Stdout = context.Stdout
62+ command.Stderr = context.Stderr
63+ // And run it!
64+ return command.Run()
65+}
66
67=== modified file 'cmd/plugins/local/main_test.go'
68--- cmd/plugins/local/main_test.go 2014-02-27 05:16:54 +0000
69+++ cmd/plugins/local/main_test.go 2014-03-20 04:14:57 +0000
70@@ -4,12 +4,17 @@
71 package local_test
72
73 import (
74+ "fmt"
75+ "os/exec"
76 "strings"
77
78+ "github.com/juju/testing"
79+ jc "github.com/juju/testing/checkers"
80 gc "launchpad.net/gocheck"
81
82+ "launchpad.net/juju-core/cmd"
83 "launchpad.net/juju-core/cmd/plugins/local"
84- "launchpad.net/juju-core/testing"
85+ coretesting "launchpad.net/juju-core/testing"
86 "launchpad.net/juju-core/testing/testbase"
87 )
88
89@@ -25,10 +30,10 @@
90 // TODO: add some as they get registered
91 }
92 plugin := local.JujuLocalPlugin()
93- ctx, err := testing.RunCommand(c, plugin, []string{"help", "commands"})
94+ ctx, err := coretesting.RunCommand(c, plugin, []string{"help", "commands"})
95 c.Assert(err, gc.IsNil)
96
97- lines := strings.Split(testing.Stdout(ctx), "\n")
98+ lines := strings.Split(coretesting.Stdout(ctx), "\n")
99 var names []string
100 for _, line := range lines {
101 f := strings.Fields(line)
102@@ -40,3 +45,34 @@
103 // The names should be output in alphabetical order, so don't sort.
104 c.Assert(names, gc.DeepEquals, expectedSubcommands)
105 }
106+
107+func (s *mainSuite) TestRunAsRootCallsFuncIfRoot(c *gc.C) {
108+ s.PatchValue(local.CheckIfRoot, func() bool { return true })
109+ called := false
110+ call := func(*cmd.Context) error {
111+ called = true
112+ return nil
113+ }
114+ args := []string{"ignored..."}
115+ err := local.RunAsRoot("juju-magic", args, coretesting.Context(c), call)
116+ c.Assert(err, gc.IsNil)
117+ c.Assert(called, jc.IsTrue)
118+}
119+
120+func (s *mainSuite) TestRunAsRootCallsSudoIfNotRoot(c *gc.C) {
121+ s.PatchValue(local.CheckIfRoot, func() bool { return false })
122+ testing.PatchExecutableAsEchoArgs(c, s, "sudo")
123+ // the command needs to be in the path...
124+ testing.PatchExecutableAsEchoArgs(c, s, "juju-magic")
125+ magicPath, err := exec.LookPath("juju-magic")
126+ c.Assert(err, gc.IsNil)
127+ callIgnored := func(*cmd.Context) error {
128+ panic("unreachable")
129+ }
130+ args := []string{"passed"}
131+ context := coretesting.Context(c)
132+ err = local.RunAsRoot("juju-magic", args, context, callIgnored)
133+ c.Assert(err, gc.IsNil)
134+ expected := fmt.Sprintf("sudo \"--preserve-env\" %q \"passed\"\n", magicPath)
135+ c.Assert(coretesting.Stdout(context), gc.Equals, expected)
136+}

Subscribers

People subscribed via source and target branches

to status/vote changes: