1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package runcommands_test
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"
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"
21
type runcommandsSuite struct {
23
remoteState remotestate.Snapshot
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)
33
var _ = gc.Suite(&runcommandsSuite{})
35
func (s *runcommandsSuite) SetUpTest(c *gc.C) {
36
s.charmURL = charm.MustParseURL("cs:precise/mysql-2")
37
s.remoteState = remotestate.Snapshot{
40
s.mockRunner = mockRunner{runCommands: func(commands string) (*exec.ExecResponse, error) {
41
return s.runCommands(commands)
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
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)
64
func (s *runcommandsSuite) TestRunCommands(c *gc.C) {
65
localState := resolver.LocalState{
67
State: operation.State{
68
Kind: operation.Continue,
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)")
80
func (s *runcommandsSuite) TestRunCommandsCallbacks(c *gc.C) {
81
var completed []string
82
s.commandCompleted = func(id string) {
83
completed = append(completed, id)
87
s.runCommands = func(commands string) (*exec.ExecResponse, error) {
88
run = append(run, commands)
89
return &exec.ExecResponse{}, nil
91
localState := resolver.LocalState{
93
State: operation.State{
94
Kind: operation.Continue,
98
id := s.commands.AddCommand(operation.CommandArgs{
99
Commands: "echo foxtrot",
100
}, func(*exec.ExecResponse, error) {})
101
s.remoteState.Commands = []string{id}
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)")
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)
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)
117
_, err = op.Commit(operation.State{})
118
c.Assert(err, jc.ErrorIsNil)
119
c.Assert(completed, jc.DeepEquals, []string{id})
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}
127
var completed []string
128
s.commandCompleted = func(id string) {
129
completed = append(completed, id)
133
s.runCommands = func(commands string) (*exec.ExecResponse, error) {
134
run = append(run, commands)
135
return &exec.ExecResponse{}, nil
137
localState := resolver.LocalState{
138
CharmURL: s.charmURL,
139
State: operation.State{
140
Kind: operation.Continue,
144
id := s.commands.AddCommand(operation.CommandArgs{
145
Commands: "echo foxtrot",
146
}, func(*exec.ExecResponse, error) {})
147
s.remoteState.Commands = []string{id}
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)")
153
_, err = op.Prepare(operation.State{})
154
c.Assert(err, jc.ErrorIsNil)
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)
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)
167
func (s *runcommandsSuite) TestRunCommandsError(c *gc.C) {
168
localState := resolver.LocalState{
169
CharmURL: s.charmURL,
170
State: operation.State{
171
Kind: operation.Continue,
174
s.runCommands = func(commands string) (*exec.ExecResponse, error) {
175
return nil, errors.Errorf("executing commands: %s", commands)
179
id := s.commands.AddCommand(operation.CommandArgs{
180
Commands: "echo foxtrot",
181
}, func(_ *exec.ExecResponse, err error) {
184
s.remoteState.Commands = []string{id}
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)")
190
_, err = op.Prepare(operation.State{})
191
c.Assert(err, jc.ErrorIsNil)
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")
198
func (s *runcommandsSuite) TestRunCommandsStatus(c *gc.C) {
199
localState := resolver.LocalState{
200
CharmURL: s.charmURL,
201
State: operation.State{
202
Kind: operation.Continue,
206
id := s.commands.AddCommand(operation.CommandArgs{
207
Commands: "echo foxtrot",
208
}, func(*exec.ExecResponse, error) {})
209
s.remoteState.Commands = []string{id}
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 */)
216
_, err = op.Prepare(operation.State{})
217
c.Assert(err, jc.ErrorIsNil)
218
s.callbacks.CheckCalls(c, nil /* no calls */)
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")
227
type commitErrorOpFactory struct {
231
func (f commitErrorOpFactory) NewCommands(args operation.CommandArgs, sendResponse operation.CommandResponseFunc) (operation.Operation, error) {
232
op, err := f.Factory.NewCommands(args, sendResponse)
234
op = commitErrorOperation{op}
239
type commitErrorOperation struct {
243
func (commitErrorOperation) Commit(operation.State) (*operation.State, error) {
244
return nil, errors.New("Commit failed")