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