github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/jujud/run_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "time" 11 12 "github.com/juju/loggo" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils/exec" 15 "github.com/juju/utils/fslock" 16 gc "launchpad.net/gocheck" 17 18 "github.com/juju/juju/cmd" 19 "github.com/juju/juju/testing" 20 "github.com/juju/juju/worker/uniter" 21 ) 22 23 type RunTestSuite struct { 24 testing.BaseSuite 25 } 26 27 var _ = gc.Suite(&RunTestSuite{}) 28 29 func (*RunTestSuite) TestWrongArgs(c *gc.C) { 30 for i, test := range []struct { 31 title string 32 args []string 33 errMatch string 34 unit string 35 commands string 36 avoidContext bool 37 }{{ 38 title: "no args", 39 errMatch: "missing unit-name", 40 }, { 41 title: "one arg", 42 args: []string{"foo"}, 43 errMatch: "missing commands", 44 }, { 45 title: "more than two arg", 46 args: []string{"foo", "bar", "baz"}, 47 errMatch: `unrecognized args: \["baz"\]`, 48 }, { 49 title: "unit and command assignment", 50 args: []string{"unit-name", "command"}, 51 unit: "unit-name", 52 commands: "command", 53 }, { 54 title: "unit id converted to tag", 55 args: []string{"foo/1", "command"}, 56 unit: "unit-foo-1", 57 commands: "command", 58 }, { 59 title: "execute not in a context", 60 args: []string{"--no-context", "command"}, 61 commands: "command", 62 avoidContext: true, 63 }, 64 } { 65 c.Logf("\n%d: %s", i, test.title) 66 runCommand := &RunCommand{} 67 err := testing.InitCommand(runCommand, test.args) 68 if test.errMatch == "" { 69 c.Assert(err, gc.IsNil) 70 c.Assert(runCommand.unit, gc.Equals, test.unit) 71 c.Assert(runCommand.commands, gc.Equals, test.commands) 72 c.Assert(runCommand.noContext, gc.Equals, test.avoidContext) 73 } else { 74 c.Assert(err, gc.ErrorMatches, test.errMatch) 75 } 76 } 77 } 78 79 func (s *RunTestSuite) TestInsideContext(c *gc.C) { 80 s.PatchEnvironment("JUJU_CONTEXT_ID", "fake-id") 81 runCommand := &RunCommand{} 82 err := runCommand.Init([]string{"foo", "bar"}) 83 c.Assert(err, gc.ErrorMatches, "juju-run cannot be called from within a hook.*") 84 } 85 86 func (s *RunTestSuite) TestMissingAgent(c *gc.C) { 87 s.PatchValue(&AgentDir, c.MkDir()) 88 89 _, err := testing.RunCommand(c, &RunCommand{}, "foo", "bar") 90 c.Assert(err, gc.ErrorMatches, `unit "foo" not found on this machine`) 91 } 92 93 func waitForResult(running <-chan *cmd.Context) (*cmd.Context, error) { 94 select { 95 case result := <-running: 96 return result, nil 97 case <-time.After(testing.ShortWait): 98 return nil, fmt.Errorf("timeout") 99 } 100 } 101 102 func startRunAsync(c *gc.C, params []string) <-chan *cmd.Context { 103 resultChannel := make(chan *cmd.Context) 104 go func() { 105 ctx, err := testing.RunCommand(c, &RunCommand{}, params...) 106 c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError) 107 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0") 108 resultChannel <- ctx 109 close(resultChannel) 110 }() 111 return resultChannel 112 } 113 114 func (s *RunTestSuite) TestNoContext(c *gc.C) { 115 s.PatchValue(&LockDir, c.MkDir()) 116 s.PatchValue(&AgentDir, c.MkDir()) 117 118 ctx, err := testing.RunCommand(c, &RunCommand{}, "--no-context", "echo done") 119 c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError) 120 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0") 121 c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") 122 } 123 124 func (s *RunTestSuite) TestNoContextAsync(c *gc.C) { 125 s.PatchValue(&LockDir, c.MkDir()) 126 s.PatchValue(&AgentDir, c.MkDir()) 127 128 channel := startRunAsync(c, []string{"--no-context", "echo done"}) 129 ctx, err := waitForResult(channel) 130 c.Assert(err, gc.IsNil) 131 c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") 132 } 133 134 func (s *RunTestSuite) TestNoContextWithLock(c *gc.C) { 135 s.PatchValue(&LockDir, c.MkDir()) 136 s.PatchValue(&AgentDir, c.MkDir()) 137 s.PatchValue(&fslock.LockWaitDelay, 10*time.Millisecond) 138 139 lock, err := getLock() 140 c.Assert(err, gc.IsNil) 141 lock.Lock("juju-run test") 142 143 channel := startRunAsync(c, []string{"--no-context", "echo done"}) 144 ctx, err := waitForResult(channel) 145 c.Assert(err, gc.ErrorMatches, "timeout") 146 147 lock.Unlock() 148 149 ctx, err = waitForResult(channel) 150 c.Assert(err, gc.IsNil) 151 c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") 152 } 153 154 func (s *RunTestSuite) TestMissingSocket(c *gc.C) { 155 s.PatchValue(&AgentDir, c.MkDir()) 156 testAgentDir := filepath.Join(AgentDir, "foo") 157 err := os.Mkdir(testAgentDir, 0755) 158 c.Assert(err, gc.IsNil) 159 160 _, err = testing.RunCommand(c, &RunCommand{}, "foo", "bar") 161 c.Assert(err, gc.ErrorMatches, `dial unix .*/run.socket: no such file or directory`) 162 } 163 164 func (s *RunTestSuite) TestRunning(c *gc.C) { 165 loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE) 166 s.runListenerForAgent(c, "foo") 167 168 ctx, err := testing.RunCommand(c, &RunCommand{}, "foo", "bar") 169 c.Check(cmd.IsRcPassthroughError(err), jc.IsTrue) 170 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 42") 171 c.Assert(testing.Stdout(ctx), gc.Equals, "bar stdout") 172 c.Assert(testing.Stderr(ctx), gc.Equals, "bar stderr") 173 } 174 175 func (s *RunTestSuite) runListenerForAgent(c *gc.C, agent string) { 176 s.PatchValue(&AgentDir, c.MkDir()) 177 178 testAgentDir := filepath.Join(AgentDir, agent) 179 err := os.Mkdir(testAgentDir, 0755) 180 c.Assert(err, gc.IsNil) 181 182 socketPath := filepath.Join(testAgentDir, uniter.RunListenerFile) 183 listener, err := uniter.NewRunListener(&mockRunner{c}, socketPath) 184 c.Assert(err, gc.IsNil) 185 c.Assert(listener, gc.NotNil) 186 s.AddCleanup(func(*gc.C) { 187 listener.Close() 188 }) 189 } 190 191 type mockRunner struct { 192 c *gc.C 193 } 194 195 var _ uniter.CommandRunner = (*mockRunner)(nil) 196 197 func (r *mockRunner) RunCommands(commands string) (results *exec.ExecResponse, err error) { 198 r.c.Log("mock runner: " + commands) 199 return &exec.ExecResponse{ 200 Code: 42, 201 Stdout: []byte(commands + " stdout"), 202 Stderr: []byte(commands + " stderr"), 203 }, nil 204 }