github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/runcommands/runcommands_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runcommands_test 5 6 import ( 7 "github.com/juju/errors" 8 jc "github.com/juju/testing/checkers" 9 "github.com/juju/utils/exec" 10 gc "gopkg.in/check.v1" 11 "gopkg.in/juju/charm.v6-unstable" 12 13 "github.com/juju/juju/worker/uniter/operation" 14 "github.com/juju/juju/worker/uniter/remotestate" 15 "github.com/juju/juju/worker/uniter/resolver" 16 "github.com/juju/juju/worker/uniter/runcommands" 17 "github.com/juju/juju/worker/uniter/runner" 18 runnercontext "github.com/juju/juju/worker/uniter/runner/context" 19 ) 20 21 type runcommandsSuite struct { 22 charmURL *charm.URL 23 remoteState remotestate.Snapshot 24 mockRunner mockRunner 25 callbacks *mockCallbacks 26 opFactory operation.Factory 27 resolver resolver.Resolver 28 commands runcommands.Commands 29 runCommands func(string) (*exec.ExecResponse, error) 30 commandCompleted func(string) 31 } 32 33 var _ = gc.Suite(&runcommandsSuite{}) 34 35 func (s *runcommandsSuite) SetUpTest(c *gc.C) { 36 s.charmURL = charm.MustParseURL("cs:precise/mysql-2") 37 s.remoteState = remotestate.Snapshot{ 38 CharmURL: s.charmURL, 39 } 40 s.mockRunner = mockRunner{runCommands: func(commands string) (*exec.ExecResponse, error) { 41 return s.runCommands(commands) 42 }} 43 s.callbacks = &mockCallbacks{} 44 s.opFactory = operation.NewFactory(operation.FactoryParams{ 45 Callbacks: s.callbacks, 46 RunnerFactory: &mockRunnerFactory{ 47 newCommandRunner: func(info runnercontext.CommandInfo) (runner.Runner, error) { 48 return &s.mockRunner, nil 49 }, 50 }, 51 }) 52 53 s.commands = runcommands.NewCommands() 54 s.commandCompleted = nil 55 s.resolver = runcommands.NewCommandsResolver( 56 s.commands, func(id string) { 57 if s.commandCompleted != nil { 58 s.commandCompleted(id) 59 } 60 }, 61 ) 62 } 63 64 func (s *runcommandsSuite) TestRunCommands(c *gc.C) { 65 localState := resolver.LocalState{ 66 CharmURL: s.charmURL, 67 State: operation.State{ 68 Kind: operation.Continue, 69 }, 70 } 71 id := s.commands.AddCommand(operation.CommandArgs{ 72 Commands: "echo foxtrot", 73 }, func(*exec.ExecResponse, error) {}) 74 s.remoteState.Commands = []string{id} 75 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 76 c.Assert(err, jc.ErrorIsNil) 77 c.Assert(op.String(), gc.Equals, "run commands (0)") 78 } 79 80 func (s *runcommandsSuite) TestRunCommandsCallbacks(c *gc.C) { 81 var completed []string 82 s.commandCompleted = func(id string) { 83 completed = append(completed, id) 84 } 85 86 var run []string 87 s.runCommands = func(commands string) (*exec.ExecResponse, error) { 88 run = append(run, commands) 89 return &exec.ExecResponse{}, nil 90 } 91 localState := resolver.LocalState{ 92 CharmURL: s.charmURL, 93 State: operation.State{ 94 Kind: operation.Continue, 95 }, 96 } 97 98 id := s.commands.AddCommand(operation.CommandArgs{ 99 Commands: "echo foxtrot", 100 }, func(*exec.ExecResponse, error) {}) 101 s.remoteState.Commands = []string{id} 102 103 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 104 c.Assert(err, jc.ErrorIsNil) 105 c.Assert(op.String(), gc.Equals, "run commands (0)") 106 107 _, err = op.Prepare(operation.State{}) 108 c.Assert(err, jc.ErrorIsNil) 109 c.Assert(run, gc.HasLen, 0) 110 c.Assert(completed, gc.HasLen, 0) 111 112 _, err = op.Execute(operation.State{}) 113 c.Assert(err, jc.ErrorIsNil) 114 c.Assert(run, jc.DeepEquals, []string{"echo foxtrot"}) 115 c.Assert(completed, gc.HasLen, 0) 116 117 _, err = op.Commit(operation.State{}) 118 c.Assert(err, jc.ErrorIsNil) 119 c.Assert(completed, jc.DeepEquals, []string{id}) 120 } 121 122 func (s *runcommandsSuite) TestRunCommandsCommitErrorNoCompletedCallback(c *gc.C) { 123 // Override opFactory with one that creates run command 124 // operations with failing Commit methods. 125 s.opFactory = commitErrorOpFactory{s.opFactory} 126 127 var completed []string 128 s.commandCompleted = func(id string) { 129 completed = append(completed, id) 130 } 131 132 var run []string 133 s.runCommands = func(commands string) (*exec.ExecResponse, error) { 134 run = append(run, commands) 135 return &exec.ExecResponse{}, nil 136 } 137 localState := resolver.LocalState{ 138 CharmURL: s.charmURL, 139 State: operation.State{ 140 Kind: operation.Continue, 141 }, 142 } 143 144 id := s.commands.AddCommand(operation.CommandArgs{ 145 Commands: "echo foxtrot", 146 }, func(*exec.ExecResponse, error) {}) 147 s.remoteState.Commands = []string{id} 148 149 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 150 c.Assert(err, jc.ErrorIsNil) 151 c.Assert(op.String(), gc.Equals, "run commands (0)") 152 153 _, err = op.Prepare(operation.State{}) 154 c.Assert(err, jc.ErrorIsNil) 155 156 _, err = op.Execute(operation.State{}) 157 c.Assert(err, jc.ErrorIsNil) 158 c.Assert(run, jc.DeepEquals, []string{"echo foxtrot"}) 159 c.Assert(completed, gc.HasLen, 0) 160 161 _, err = op.Commit(operation.State{}) 162 c.Assert(err, gc.ErrorMatches, "Commit failed") 163 // commandCompleted is not called if Commit fails 164 c.Assert(completed, gc.HasLen, 0) 165 } 166 167 func (s *runcommandsSuite) TestRunCommandsError(c *gc.C) { 168 localState := resolver.LocalState{ 169 CharmURL: s.charmURL, 170 State: operation.State{ 171 Kind: operation.Continue, 172 }, 173 } 174 s.runCommands = func(commands string) (*exec.ExecResponse, error) { 175 return nil, errors.Errorf("executing commands: %s", commands) 176 } 177 178 var execErr error 179 id := s.commands.AddCommand(operation.CommandArgs{ 180 Commands: "echo foxtrot", 181 }, func(_ *exec.ExecResponse, err error) { 182 execErr = err 183 }) 184 s.remoteState.Commands = []string{id} 185 186 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 187 c.Assert(err, jc.ErrorIsNil) 188 c.Assert(op.String(), gc.Equals, "run commands (0)") 189 190 _, err = op.Prepare(operation.State{}) 191 c.Assert(err, jc.ErrorIsNil) 192 193 _, err = op.Execute(operation.State{}) 194 c.Assert(execErr, gc.ErrorMatches, "executing commands: echo foxtrot") 195 c.Assert(execErr, gc.ErrorMatches, "executing commands: echo foxtrot") 196 } 197 198 func (s *runcommandsSuite) TestRunCommandsStatus(c *gc.C) { 199 localState := resolver.LocalState{ 200 CharmURL: s.charmURL, 201 State: operation.State{ 202 Kind: operation.Continue, 203 }, 204 } 205 206 id := s.commands.AddCommand(operation.CommandArgs{ 207 Commands: "echo foxtrot", 208 }, func(*exec.ExecResponse, error) {}) 209 s.remoteState.Commands = []string{id} 210 211 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 212 c.Assert(err, jc.ErrorIsNil) 213 c.Assert(op.String(), gc.Equals, "run commands (0)") 214 s.callbacks.CheckCalls(c, nil /* no calls */) 215 216 _, err = op.Prepare(operation.State{}) 217 c.Assert(err, jc.ErrorIsNil) 218 s.callbacks.CheckCalls(c, nil /* no calls */) 219 220 s.callbacks.SetErrors(errors.New("cannot set status")) 221 _, err = op.Execute(operation.State{}) 222 c.Assert(err, gc.ErrorMatches, "cannot set status") 223 s.callbacks.CheckCallNames(c, "SetExecutingStatus") 224 s.callbacks.CheckCall(c, 0, "SetExecutingStatus", "running commands") 225 } 226 227 type commitErrorOpFactory struct { 228 operation.Factory 229 } 230 231 func (f commitErrorOpFactory) NewCommands(args operation.CommandArgs, sendResponse operation.CommandResponseFunc) (operation.Operation, error) { 232 op, err := f.Factory.NewCommands(args, sendResponse) 233 if err == nil { 234 op = commitErrorOperation{op} 235 } 236 return op, err 237 } 238 239 type commitErrorOperation struct { 240 operation.Operation 241 } 242 243 func (commitErrorOperation) Commit(operation.State) (*operation.State, error) { 244 return nil, errors.New("Commit failed") 245 }