github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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/cmd" 13 "github.com/juju/loggo" 14 "github.com/juju/names" 15 jc "github.com/juju/testing/checkers" 16 "github.com/juju/utils" 17 "github.com/juju/utils/exec" 18 "github.com/juju/utils/fslock" 19 gc "gopkg.in/check.v1" 20 21 cmdutil "github.com/juju/juju/cmd/jujud/util" 22 "github.com/juju/juju/testing" 23 "github.com/juju/juju/version" 24 "github.com/juju/juju/worker/uniter" 25 ) 26 27 type RunTestSuite struct { 28 testing.BaseSuite 29 } 30 31 func (s *RunTestSuite) SetUpTest(c *gc.C) { 32 s.BaseSuite.SetUpTest(c) 33 s.PatchValue(&cmdutil.DataDir, c.MkDir()) 34 } 35 36 var _ = gc.Suite(&RunTestSuite{}) 37 38 func (*RunTestSuite) TestArgParsing(c *gc.C) { 39 for i, test := range []struct { 40 title string 41 args []string 42 errMatch string 43 unit names.UnitTag 44 commands string 45 avoidContext bool 46 relationId string 47 remoteUnit string 48 forceRemoteUnit bool 49 }{{ 50 title: "no args", 51 errMatch: "missing unit-name", 52 }, { 53 title: "one arg", 54 args: []string{"foo"}, 55 errMatch: `"foo" is not a valid tag`, 56 }, { 57 title: "one arg", 58 args: []string{"foo/2"}, 59 errMatch: "missing commands", 60 }, { 61 title: "more than two arg", 62 args: []string{"foo/2", "bar", "baz"}, 63 errMatch: `unrecognized args: \["baz"\]`, 64 }, { 65 title: "unit and command assignment", 66 args: []string{"unit-name-2", "command"}, 67 unit: names.NewUnitTag("name/2"), 68 commands: "command", 69 relationId: "", 70 }, { 71 title: "unit id converted to tag", 72 args: []string{"foo/1", "command"}, 73 unit: names.NewUnitTag("foo/1"), 74 commands: "command", 75 relationId: "", 76 }, { 77 title: "execute not in a context", 78 args: []string{"--no-context", "command"}, 79 commands: "command", 80 avoidContext: true, 81 relationId: "", 82 forceRemoteUnit: false, 83 }, { 84 title: "relation-id", 85 args: []string{"--relation", "db:1", "unit-name-2", "command"}, 86 commands: "command", 87 unit: names.NewUnitTag("name/2"), 88 relationId: "db:1", 89 remoteUnit: "", 90 avoidContext: false, 91 forceRemoteUnit: false, 92 }, { 93 title: "remote-unit", 94 args: []string{"--remote-unit", "unit-name-1", "unit-name-2", "command"}, 95 commands: "command", 96 unit: names.NewUnitTag("name/2"), 97 avoidContext: false, 98 relationId: "", 99 remoteUnit: "unit-name-1", 100 forceRemoteUnit: false, 101 }, { 102 title: "no-remote-unit", 103 args: []string{"--force-remote-unit", "--relation", "mongodb:1", "unit-name-2", "command"}, 104 commands: "command", 105 unit: names.NewUnitTag("name/2"), 106 relationId: "mongodb:1", 107 forceRemoteUnit: true, 108 }, 109 } { 110 c.Logf("\n%d: %s", i, test.title) 111 runCommand := &RunCommand{} 112 err := testing.InitCommand(runCommand, test.args) 113 if test.errMatch == "" { 114 c.Assert(err, jc.ErrorIsNil) 115 c.Assert(runCommand.unit, gc.Equals, test.unit) 116 c.Assert(runCommand.commands, gc.Equals, test.commands) 117 c.Assert(runCommand.noContext, gc.Equals, test.avoidContext) 118 c.Assert(runCommand.relationId, gc.Equals, test.relationId) 119 c.Assert(runCommand.remoteUnitName, gc.Equals, test.remoteUnit) 120 c.Assert(runCommand.forceRemoteUnit, gc.Equals, test.forceRemoteUnit) 121 } else { 122 c.Assert(err, gc.ErrorMatches, test.errMatch) 123 } 124 } 125 } 126 127 func (s *RunTestSuite) TestInsideContext(c *gc.C) { 128 s.PatchEnvironment("JUJU_CONTEXT_ID", "fake-id") 129 runCommand := &RunCommand{} 130 err := runCommand.Init([]string{"foo", "bar"}) 131 c.Assert(err, gc.ErrorMatches, "juju-run cannot be called from within a hook.*") 132 } 133 134 func (s *RunTestSuite) TestMissingAgentName(c *gc.C) { 135 _, err := testing.RunCommand(c, &RunCommand{}, "foo/2", "bar") 136 c.Assert(err, gc.ErrorMatches, `unit "foo/2" not found on this machine`) 137 } 138 139 func (s *RunTestSuite) TestMissingAgentTag(c *gc.C) { 140 _, err := testing.RunCommand(c, &RunCommand{}, "unit-foo-2", "bar") 141 c.Assert(err, gc.ErrorMatches, `unit "foo/2" not found on this machine`) 142 } 143 144 func waitForResult(running <-chan *cmd.Context, timeout time.Duration) (*cmd.Context, error) { 145 select { 146 case result := <-running: 147 return result, nil 148 case <-time.After(timeout): 149 return nil, fmt.Errorf("timeout") 150 } 151 } 152 153 func startRunAsync(c *gc.C, params []string) <-chan *cmd.Context { 154 resultChannel := make(chan *cmd.Context) 155 go func() { 156 ctx, err := testing.RunCommand(c, &RunCommand{}, params...) 157 c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError) 158 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0") 159 resultChannel <- ctx 160 close(resultChannel) 161 }() 162 return resultChannel 163 } 164 165 func (s *RunTestSuite) TestNoContext(c *gc.C) { 166 ctx, err := testing.RunCommand(c, &RunCommand{}, "--no-context", "echo done") 167 c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError) 168 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0") 169 c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") 170 } 171 172 func (s *RunTestSuite) TestNoContextAsync(c *gc.C) { 173 channel := startRunAsync(c, []string{"--no-context", "echo done"}) 174 ctx, err := waitForResult(channel, testing.LongWait) 175 c.Assert(err, jc.ErrorIsNil) 176 c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") 177 } 178 179 func (s *RunTestSuite) TestNoContextWithLock(c *gc.C) { 180 s.PatchValue(&fslock.LockWaitDelay, 10*time.Millisecond) 181 182 lock, err := cmdutil.HookExecutionLock(cmdutil.DataDir) 183 c.Assert(err, jc.ErrorIsNil) 184 lock.Lock("juju-run test") 185 defer lock.Unlock() // in case of failure 186 187 channel := startRunAsync(c, []string{"--no-context", "echo done"}) 188 ctx, err := waitForResult(channel, testing.ShortWait) 189 c.Assert(err, gc.ErrorMatches, "timeout") 190 191 lock.Unlock() 192 193 ctx, err = waitForResult(channel, testing.LongWait) 194 c.Assert(err, jc.ErrorIsNil) 195 c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") 196 } 197 198 func (s *RunTestSuite) TestMissingSocket(c *gc.C) { 199 agentDir := filepath.Join(cmdutil.DataDir, "agents", "unit-foo-1") 200 err := os.MkdirAll(agentDir, 0755) 201 c.Assert(err, jc.ErrorIsNil) 202 203 _, err = testing.RunCommand(c, &RunCommand{}, "foo/1", "bar") 204 c.Assert(err, gc.ErrorMatches, `dial unix .*/run.socket: `+utils.NoSuchFileErrRegexp) 205 } 206 207 func (s *RunTestSuite) TestRunning(c *gc.C) { 208 loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE) 209 s.runListenerForAgent(c, "unit-foo-1") 210 211 ctx, err := testing.RunCommand(c, &RunCommand{}, "foo/1", "bar") 212 c.Check(cmd.IsRcPassthroughError(err), jc.IsTrue) 213 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 42") 214 c.Assert(testing.Stdout(ctx), gc.Equals, "bar stdout") 215 c.Assert(testing.Stderr(ctx), gc.Equals, "bar stderr") 216 } 217 218 func (s *RunTestSuite) TestRunningRelation(c *gc.C) { 219 loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE) 220 s.runListenerForAgent(c, "unit-foo-1") 221 222 ctx, err := testing.RunCommand(c, &RunCommand{}, "--relation", "db:1", "foo/1", "bar") 223 c.Check(cmd.IsRcPassthroughError(err), jc.IsTrue) 224 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 42") 225 c.Assert(testing.Stdout(ctx), gc.Equals, "bar stdout") 226 c.Assert(testing.Stderr(ctx), gc.Equals, "bar stderr") 227 } 228 229 func (s *RunTestSuite) TestRunningBadRelation(c *gc.C) { 230 loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE) 231 s.runListenerForAgent(c, "unit-foo-1") 232 233 _, err := testing.RunCommand(c, &RunCommand{}, "--relation", "badrelation:W", "foo/1", "bar") 234 c.Check(cmd.IsRcPassthroughError(err), jc.IsFalse) 235 c.Assert(err, gc.ErrorMatches, "invalid relation id") 236 } 237 238 func (s *RunTestSuite) TestRunningRemoteUnitNoRelation(c *gc.C) { 239 loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE) 240 s.runListenerForAgent(c, "unit-foo-1") 241 242 _, err := testing.RunCommand(c, &RunCommand{}, "--remote-unit", "remote/0", "foo/1", "bar") 243 c.Check(cmd.IsRcPassthroughError(err), jc.IsFalse) 244 c.Assert(err, gc.ErrorMatches, "remote unit: remote/0, provided without a relation") 245 } 246 247 func (s *RunTestSuite) TestSkipCheckAndRemoteUnit(c *gc.C) { 248 loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE) 249 s.runListenerForAgent(c, "unit-foo-1") 250 251 ctx, err := testing.RunCommand(c, &RunCommand{}, "--force-remote-unit", "--remote-unit", "unit-name-2", "--relation", "db:1", "foo/1", "bar") 252 c.Check(cmd.IsRcPassthroughError(err), jc.IsTrue) 253 c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 42") 254 c.Assert(testing.Stdout(ctx), gc.Equals, "bar stdout") 255 c.Assert(testing.Stderr(ctx), gc.Equals, "bar stderr") 256 } 257 258 func (s *RunTestSuite) TestCheckRelationIdValid(c *gc.C) { 259 for i, test := range []struct { 260 title string 261 input string 262 output int 263 err bool 264 }{ 265 { 266 title: "valid, id only", 267 input: "0", 268 output: 0, 269 err: false, 270 }, { 271 title: "valid, relation:id", 272 input: "db:1", 273 output: 1, 274 err: false, 275 }, { 276 title: "not valid, just relation", 277 input: "db", 278 output: -1, 279 err: true, 280 }, { 281 title: "not valud, malformed relation:id", 282 input: "db:X", 283 output: -1, 284 err: true, 285 }, 286 } { 287 c.Logf("\n%d: %s", i, test.title) 288 relationId, err := checkRelationId(test.input) 289 c.Assert(relationId, gc.Equals, test.output) 290 if test.err { 291 c.Assert(err, gc.NotNil) 292 } 293 } 294 } 295 296 func (s *RunTestSuite) runListenerForAgent(c *gc.C, agent string) { 297 agentDir := filepath.Join(cmdutil.DataDir, "agents", agent) 298 err := os.MkdirAll(agentDir, 0755) 299 c.Assert(err, jc.ErrorIsNil) 300 var socketPath string 301 switch version.Current.OS { 302 case version.Windows: 303 socketPath = fmt.Sprintf(`\\.\pipe\%s-run`, agent) 304 default: 305 socketPath = fmt.Sprintf("%s/run.socket", agentDir) 306 } 307 listener, err := uniter.NewRunListener(&mockRunner{c}, socketPath) 308 c.Assert(err, jc.ErrorIsNil) 309 c.Assert(listener, gc.NotNil) 310 s.AddCleanup(func(*gc.C) { 311 listener.Close() 312 }) 313 } 314 315 type mockRunner struct { 316 c *gc.C 317 } 318 319 var _ uniter.CommandRunner = (*mockRunner)(nil) 320 321 func (r *mockRunner) RunCommands(args uniter.RunCommandsArgs) (results *exec.ExecResponse, err error) { 322 r.c.Log("mock runner: " + args.Commands) 323 return &exec.ExecResponse{ 324 Code: 42, 325 Stdout: []byte(args.Commands + " stdout"), 326 Stderr: []byte(args.Commands + " stderr"), 327 }, nil 328 }