github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/action/operation_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package action_test
     5  
     6  import (
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/kr/pretty"
    14  	"go.uber.org/mock/gomock"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	facademocks "github.com/juju/juju/apiserver/facade/mocks"
    18  	"github.com/juju/juju/apiserver/facades/client/action"
    19  	"github.com/juju/juju/rpc/params"
    20  	"github.com/juju/juju/state"
    21  )
    22  
    23  type operationSuite struct {
    24  	baseSuite
    25  }
    26  
    27  var _ = gc.Suite(&operationSuite{})
    28  
    29  func (s *operationSuite) setupOperations(c *gc.C) {
    30  	parallel := true
    31  	executionGroup := "group"
    32  	arg := params.Actions{
    33  		Actions: []params.Action{
    34  			{Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{},
    35  				Parallel: &parallel, ExecutionGroup: &executionGroup},
    36  			{Receiver: s.mysqlUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}},
    37  			{Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}},
    38  			{Receiver: s.mysqlUnit.Tag().String(), Name: "anotherfakeaction", Parameters: map[string]interface{}{}},
    39  		}}
    40  
    41  	r, err := s.action.EnqueueOperation(arg)
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	c.Assert(r.Actions, gc.HasLen, len(arg.Actions))
    44  
    45  	// There's only one operation created.
    46  	ops, err := s.Model.AllOperations()
    47  	c.Assert(err, jc.ErrorIsNil)
    48  	c.Assert(ops, gc.HasLen, 1)
    49  	operationID, err := strconv.Atoi(ops[0].Id())
    50  	c.Assert(err, jc.ErrorIsNil)
    51  
    52  	a, err := s.Model.Action(strconv.Itoa(operationID + 1))
    53  	c.Assert(err, jc.ErrorIsNil)
    54  	c.Assert(a.Parallel(), jc.IsTrue)
    55  	c.Assert(a.ExecutionGroup(), gc.Equals, "group")
    56  	_, err = a.Begin()
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	a, err = s.Model.Action(strconv.Itoa(operationID + 2))
    59  	c.Assert(err, jc.ErrorIsNil)
    60  	_, err = a.Finish(state.ActionResults{Status: state.ActionCompleted})
    61  	c.Assert(err, jc.ErrorIsNil)
    62  }
    63  
    64  func (s *operationSuite) TestListOperationsStatusFilter(c *gc.C) {
    65  	s.setupOperations(c)
    66  	// Set up a non running operation.
    67  	arg := params.Actions{
    68  		Actions: []params.Action{
    69  			{Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}},
    70  		}}
    71  	_, err := s.action.EnqueueOperation(arg)
    72  	c.Assert(err, jc.ErrorIsNil)
    73  
    74  	operations, err := s.action.ListOperations(params.OperationQueryArgs{
    75  		Status: []string{"running"},
    76  	})
    77  	c.Assert(err, jc.ErrorIsNil)
    78  	c.Assert(operations.Truncated, jc.IsFalse)
    79  	c.Assert(operations.Results, gc.HasLen, 1)
    80  	result := operations.Results[0]
    81  	c.Assert(result.Actions, gc.HasLen, 4)
    82  	c.Assert(result.Actions[0].Action, gc.NotNil)
    83  	if result.Enqueued.IsZero() {
    84  		c.Fatal("enqueued time not set")
    85  	}
    86  	if result.Started.IsZero() {
    87  		c.Fatal("started time not set")
    88  	}
    89  	c.Assert(result.Status, gc.Equals, "running")
    90  
    91  	action := result.Actions[0].Action
    92  	c.Assert(action.Name, gc.Equals, "fakeaction")
    93  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
    94  	c.Assert(action.Tag, gc.Equals, "action-2")
    95  	c.Assert(result.Actions[0].Status, gc.Equals, "running")
    96  	action = result.Actions[1].Action
    97  	c.Assert(action.Name, gc.Equals, "fakeaction")
    98  	c.Assert(action.Receiver, gc.Equals, "unit-mysql-0")
    99  	c.Assert(action.Tag, gc.Equals, "action-3")
   100  	c.Assert(result.Actions[1].Status, gc.Equals, "completed")
   101  	action = result.Actions[2].Action
   102  	c.Assert(action.Name, gc.Equals, "fakeaction")
   103  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   104  	c.Assert(action.Tag, gc.Equals, "action-4")
   105  	c.Assert(result.Actions[2].Status, gc.Equals, "pending")
   106  	action = result.Actions[3].Action
   107  	c.Assert(action.Name, gc.Equals, "anotherfakeaction")
   108  	c.Assert(action.Receiver, gc.Equals, "unit-mysql-0")
   109  	c.Assert(action.Tag, gc.Equals, "action-5")
   110  	c.Assert(result.Actions[3].Status, gc.Equals, "pending")
   111  }
   112  
   113  func (s *operationSuite) TestListOperationsNameFilter(c *gc.C) {
   114  	s.setupOperations(c)
   115  	// Set up a second operation.
   116  	arg := params.Actions{
   117  		Actions: []params.Action{
   118  			{Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}},
   119  		}}
   120  	_, err := s.action.EnqueueOperation(arg)
   121  	c.Assert(err, jc.ErrorIsNil)
   122  
   123  	operations, err := s.action.ListOperations(params.OperationQueryArgs{
   124  		ActionNames: []string{"anotherfakeaction"},
   125  	})
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	c.Assert(operations.Results, gc.HasLen, 1)
   128  	result := operations.Results[0]
   129  	c.Assert(result.Actions, gc.HasLen, 1)
   130  	c.Assert(result.Actions[0].Action, gc.NotNil)
   131  	if result.Enqueued.IsZero() {
   132  		c.Fatal("enqueued time not set")
   133  	}
   134  	if result.Started.IsZero() {
   135  		c.Fatal("started time not set")
   136  	}
   137  	c.Assert(result.Status, gc.Equals, "running")
   138  	action := result.Actions[0].Action
   139  	c.Assert(action.Name, gc.Equals, "anotherfakeaction")
   140  	c.Assert(action.Receiver, gc.Equals, "unit-mysql-0")
   141  	c.Assert(action.Tag, gc.Equals, "action-5")
   142  	c.Assert(result.Actions[0].Status, gc.Equals, "pending")
   143  }
   144  
   145  func (s *operationSuite) TestListOperationsAppFilter(c *gc.C) {
   146  	s.setupOperations(c)
   147  	// Set up a second operation for a different app.
   148  	arg := params.Actions{
   149  		Actions: []params.Action{
   150  			{Receiver: s.mysqlUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}},
   151  		}}
   152  	_, err := s.action.EnqueueOperation(arg)
   153  	c.Assert(err, jc.ErrorIsNil)
   154  
   155  	operations, err := s.action.ListOperations(params.OperationQueryArgs{
   156  		Applications: []string{"wordpress"},
   157  	})
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	c.Assert(operations.Results, gc.HasLen, 1)
   160  	result := operations.Results[0]
   161  
   162  	c.Assert(result.Actions, gc.HasLen, 2)
   163  	c.Assert(result.Actions[0].Action, gc.NotNil)
   164  	if result.Enqueued.IsZero() {
   165  		c.Fatal("enqueued time not set")
   166  	}
   167  	if result.Started.IsZero() {
   168  		c.Fatal("started time not set")
   169  	}
   170  	c.Assert(result.Status, gc.Equals, "running")
   171  	action := result.Actions[0].Action
   172  	c.Assert(action.Name, gc.Equals, "fakeaction")
   173  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   174  	c.Assert(action.Tag, gc.Equals, "action-2")
   175  	c.Assert(result.Actions[0].Status, gc.Equals, "running")
   176  	action = result.Actions[1].Action
   177  	c.Assert(action.Name, gc.Equals, "fakeaction")
   178  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   179  	c.Assert(action.Tag, gc.Equals, "action-4")
   180  	c.Assert(result.Actions[1].Status, gc.Equals, "pending")
   181  }
   182  
   183  func (s *operationSuite) TestListOperationsUnitFilter(c *gc.C) {
   184  	s.setupOperations(c)
   185  	// Set up an operation with a pending action.
   186  	arg := params.Actions{
   187  		Actions: []params.Action{
   188  			{Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}},
   189  		}}
   190  	_, err := s.action.EnqueueOperation(arg)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	operations, err := s.action.ListOperations(params.OperationQueryArgs{
   194  		Units:  []string{"wordpress/0"},
   195  		Status: []string{"pending"},
   196  	})
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Assert(operations.Results, gc.HasLen, 1)
   199  	result := operations.Results[0]
   200  
   201  	c.Assert(result.Actions, gc.HasLen, 1)
   202  	c.Assert(result.Actions[0].Action, gc.NotNil)
   203  	if result.Enqueued.IsZero() {
   204  		c.Fatal("enqueued time not set")
   205  	}
   206  	c.Assert(result.Status, gc.Equals, "pending")
   207  	action := result.Actions[0].Action
   208  	c.Assert(action.Name, gc.Equals, "fakeaction")
   209  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   210  	c.Assert(action.Tag, gc.Equals, "action-7")
   211  	c.Assert(result.Actions[0].Status, gc.Equals, "pending")
   212  }
   213  
   214  func (s *operationSuite) TestListOperationsMachineFilter(c *gc.C) {
   215  	s.setupOperations(c)
   216  	// Set up an operation with a pending action.
   217  	arg := params.Actions{
   218  		Actions: []params.Action{
   219  			{Receiver: s.machine0.Tag().String(), Name: "juju-exec", Parameters: map[string]interface{}{
   220  				"command": "ls",
   221  				"timeout": 1,
   222  			}},
   223  		}}
   224  	_, err := s.action.EnqueueOperation(arg)
   225  	c.Assert(err, jc.ErrorIsNil)
   226  
   227  	operations, err := s.action.ListOperations(params.OperationQueryArgs{
   228  		Machines: []string{"0"},
   229  		Status:   []string{"pending"},
   230  	})
   231  	c.Assert(err, jc.ErrorIsNil)
   232  	c.Assert(operations.Results, gc.HasLen, 1)
   233  	result := operations.Results[0]
   234  
   235  	c.Assert(result.Actions, gc.HasLen, 1)
   236  	c.Assert(result.Actions[0].Action, gc.NotNil)
   237  	if result.Enqueued.IsZero() {
   238  		c.Fatal("enqueued time not set")
   239  	}
   240  	c.Assert(result.Status, gc.Equals, "pending")
   241  	action := result.Actions[0].Action
   242  	c.Assert(action.Name, gc.Equals, "juju-exec")
   243  	c.Assert(action.Receiver, gc.Equals, "machine-0")
   244  	c.Assert(action.Tag, gc.Equals, "action-7")
   245  	c.Assert(result.Actions[0].Status, gc.Equals, "pending")
   246  }
   247  
   248  func (s *operationSuite) TestListOperationsAppAndUnitFilter(c *gc.C) {
   249  	s.setupOperations(c)
   250  	// Set up an operation with a pending action.
   251  	arg := params.Actions{
   252  		Actions: []params.Action{
   253  			{Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}},
   254  		}}
   255  	_, err := s.action.EnqueueOperation(arg)
   256  	c.Assert(err, jc.ErrorIsNil)
   257  
   258  	operations, err := s.action.ListOperations(params.OperationQueryArgs{
   259  		Applications: []string{"mysql"},
   260  		Units:        []string{"wordpress/0"},
   261  		Status:       []string{"running"},
   262  	})
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	c.Assert(operations.Results, gc.HasLen, 1)
   265  	c.Log(pretty.Sprint(operations.Results))
   266  	result := operations.Results[0]
   267  
   268  	c.Assert(result.Actions, gc.HasLen, 4)
   269  	c.Assert(result.Actions[0].Action, gc.NotNil)
   270  	if result.Enqueued.IsZero() {
   271  		c.Fatal("enqueued time not set")
   272  	}
   273  	if result.Started.IsZero() {
   274  		c.Fatal("started time not set")
   275  	}
   276  
   277  	action := result.Actions[0].Action
   278  	c.Assert(action.Name, gc.Equals, "fakeaction")
   279  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   280  	c.Assert(action.Tag, gc.Equals, "action-2")
   281  	c.Assert(result.Actions[0].Status, gc.Equals, "running")
   282  	action = result.Actions[1].Action
   283  	c.Assert(action.Name, gc.Equals, "fakeaction")
   284  	c.Assert(action.Receiver, gc.Equals, "unit-mysql-0")
   285  	c.Assert(action.Tag, gc.Equals, "action-3")
   286  	c.Assert(result.Actions[1].Status, gc.Equals, "completed")
   287  	action = result.Actions[2].Action
   288  	c.Assert(action.Name, gc.Equals, "fakeaction")
   289  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   290  	c.Assert(action.Tag, gc.Equals, "action-4")
   291  	c.Assert(result.Actions[2].Status, gc.Equals, "pending")
   292  	action = result.Actions[3].Action
   293  	c.Assert(action.Name, gc.Equals, "anotherfakeaction")
   294  	c.Assert(action.Receiver, gc.Equals, "unit-mysql-0")
   295  	c.Assert(action.Tag, gc.Equals, "action-5")
   296  	c.Assert(result.Actions[3].Status, gc.Equals, "pending")
   297  }
   298  
   299  func (s *operationSuite) TestOperations(c *gc.C) {
   300  	s.setupOperations(c)
   301  	operations, err := s.action.Operations(params.Entities{
   302  		Entities: []params.Entity{{Tag: "operation-1"}},
   303  	})
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	c.Assert(operations.Truncated, jc.IsFalse)
   306  	c.Assert(operations.Results, gc.HasLen, 1)
   307  	result := operations.Results[0]
   308  	c.Assert(result.Actions, gc.HasLen, 4)
   309  	c.Assert(result.Actions[0].Action, gc.NotNil)
   310  	if result.Enqueued.IsZero() {
   311  		c.Fatal("enqueued time not set")
   312  	}
   313  	if result.Started.IsZero() {
   314  		c.Fatal("started time not set")
   315  	}
   316  	c.Assert(result.Status, gc.Equals, "running")
   317  
   318  	action := result.Actions[0].Action
   319  	c.Assert(action.Name, gc.Equals, "fakeaction")
   320  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   321  	c.Assert(action.Tag, gc.Equals, "action-2")
   322  	c.Assert(result.Actions[0].Status, gc.Equals, "running")
   323  	action = result.Actions[1].Action
   324  	c.Assert(action.Name, gc.Equals, "fakeaction")
   325  	c.Assert(action.Receiver, gc.Equals, "unit-mysql-0")
   326  	c.Assert(action.Tag, gc.Equals, "action-3")
   327  	c.Assert(result.Actions[1].Status, gc.Equals, "completed")
   328  	action = result.Actions[2].Action
   329  	c.Assert(action.Name, gc.Equals, "fakeaction")
   330  	c.Assert(action.Receiver, gc.Equals, "unit-wordpress-0")
   331  	c.Assert(action.Tag, gc.Equals, "action-4")
   332  	c.Assert(result.Actions[2].Status, gc.Equals, "pending")
   333  	action = result.Actions[3].Action
   334  	c.Assert(action.Name, gc.Equals, "anotherfakeaction")
   335  	c.Assert(action.Receiver, gc.Equals, "unit-mysql-0")
   336  	c.Assert(action.Tag, gc.Equals, "action-5")
   337  	c.Assert(result.Actions[3].Status, gc.Equals, "pending")
   338  }
   339  
   340  type enqueueSuite struct {
   341  	action.MockBaseSuite
   342  
   343  	wordpressAction *action.MockAction
   344  	mysqlAction     *action.MockAction
   345  	model           *action.MockModel
   346  
   347  	modelTag         names.ModelTag
   348  	wordpressUnitTag names.UnitTag
   349  	mysqlUnitTag     names.UnitTag
   350  	executionGroup   string
   351  }
   352  
   353  var _ = gc.Suite(&enqueueSuite{})
   354  
   355  func (s *enqueueSuite) SetUpSuite(c *gc.C) {
   356  	s.modelTag = names.NewModelTag("model-tag")
   357  	// mysql will be parallel false
   358  	s.wordpressUnitTag = names.NewUnitTag("wordpress/0")
   359  	// mysql will be parallel true
   360  	s.mysqlUnitTag = names.NewUnitTag("mysql/0")
   361  	s.executionGroup = "testgroup"
   362  }
   363  
   364  func (s *enqueueSuite) TestEnqueueOperation(c *gc.C) {
   365  	ctrl := s.setupMocks(c)
   366  	defer ctrl.Finish()
   367  
   368  	s.model.EXPECT().EnqueueOperation(gomock.Any(), 2).Return("1", nil)
   369  	s.expectWordpressActionResult()
   370  	s.expectMysqlActionResult()
   371  
   372  	api := s.NewActionAPI(c)
   373  
   374  	expectedName := "fakeaction"
   375  	f := false
   376  	t := true
   377  	arg := params.Actions{
   378  		Actions: []params.Action{
   379  			{
   380  				Receiver:       s.wordpressUnitTag.String(),
   381  				Name:           expectedName,
   382  				Parameters:     map[string]interface{}{},
   383  				Parallel:       &f,
   384  				ExecutionGroup: &s.executionGroup,
   385  			}, {
   386  				Receiver:       s.mysqlUnitTag.String(),
   387  				Name:           expectedName,
   388  				Parameters:     map[string]interface{}{},
   389  				Parallel:       &t,
   390  				ExecutionGroup: &s.executionGroup,
   391  			},
   392  		}}
   393  
   394  	r, err := api.EnqueueOperation(arg)
   395  	c.Assert(err, jc.ErrorIsNil)
   396  	c.Assert(r.Actions, gc.HasLen, len(arg.Actions))
   397  	c.Assert(r.Actions[0].Status, gc.Equals, "running")
   398  	c.Assert(r.Actions[0].Action.Name, gc.Equals, expectedName)
   399  	c.Assert(r.Actions[0].Action.Tag, gc.Equals, "action-2")
   400  	c.Assert(r.Actions[1].Status, gc.Equals, "running")
   401  	c.Assert(r.Actions[1].Action.Name, gc.Equals, expectedName)
   402  	c.Assert(r.Actions[1].Action.Tag, gc.Equals, "action-3")
   403  }
   404  
   405  func (s *enqueueSuite) TestEnqueueOperationFail(c *gc.C) {
   406  	ctrl := s.setupMocks(c)
   407  	defer ctrl.Finish()
   408  
   409  	expectedName := "fakeaction"
   410  	s.model.EXPECT().EnqueueOperation(gomock.Any(), 3).Return("1", nil)
   411  	s.expectWordpressActionResult()
   412  	s.model.EXPECT().AddAction(gomock.Any(), "1", expectedName, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.NotFoundf("database txn failure"))
   413  	leaders := map[string]string{
   414  		"test": "test/1",
   415  	}
   416  	s.Leadership.EXPECT().Leaders().Return(leaders, nil)
   417  	s.model.EXPECT().FailOperationEnqueuing("1", "error(s) enqueueing action(s): database txn failure not found, could not determine leader for \"mysql\"", 1)
   418  
   419  	api := s.NewActionAPI(c)
   420  
   421  	f := false
   422  	arg := params.Actions{
   423  		Actions: []params.Action{
   424  			{
   425  				Receiver:       s.wordpressUnitTag.String(),
   426  				Name:           expectedName,
   427  				Parameters:     map[string]interface{}{},
   428  				Parallel:       &f,
   429  				ExecutionGroup: &s.executionGroup,
   430  			},
   431  			// AddAction failure.
   432  			{Receiver: s.mysqlUnitTag.String(), Name: expectedName, Parameters: map[string]interface{}{}},
   433  			// Leader failure
   434  			{Receiver: "mysql/leader", Name: expectedName, Parameters: map[string]interface{}{}},
   435  		}}
   436  
   437  	r, err := api.EnqueueOperation(arg)
   438  	c.Assert(err, jc.ErrorIsNil)
   439  	c.Assert(r.Actions, gc.HasLen, len(arg.Actions))
   440  	c.Logf("%s", pretty.Sprint(r.Actions))
   441  	c.Assert(r.Actions[0].Status, gc.Equals, "running")
   442  	c.Assert(r.Actions[0].Action.Name, gc.Equals, expectedName)
   443  	c.Assert(r.Actions[0].Action.Tag, gc.Equals, "action-2")
   444  	c.Assert(r.Actions[1].Error, jc.Satisfies, params.IsCodeNotFoundOrCodeUnauthorized)
   445  	c.Assert(r.Actions[2].Error, gc.DeepEquals, &params.Error{Message: "could not determine leader for \"mysql\"", Code: ""})
   446  }
   447  
   448  func (s *enqueueSuite) TestEnqueueOperationLeadership(c *gc.C) {
   449  	ctrl := s.setupMocks(c)
   450  	defer ctrl.Finish()
   451  
   452  	s.model.EXPECT().EnqueueOperation(gomock.Any(), 2).Return("1", nil)
   453  	appName, _ := names.UnitApplication(s.mysqlUnitTag.Id())
   454  	leaders := map[string]string{
   455  		"test":  "test/1",
   456  		appName: s.mysqlUnitTag.Id(),
   457  	}
   458  	s.Leadership.EXPECT().Leaders().Return(leaders, nil)
   459  	s.expectWordpressActionResult()
   460  	s.expectMysqlActionResult()
   461  
   462  	api := s.NewActionAPI(c)
   463  
   464  	expectedName := "fakeaction"
   465  	f := false
   466  	t := true
   467  	arg := params.Actions{
   468  		Actions: []params.Action{
   469  			{
   470  				Receiver:       s.wordpressUnitTag.String(),
   471  				Name:           expectedName,
   472  				Parameters:     map[string]interface{}{},
   473  				Parallel:       &f,
   474  				ExecutionGroup: &s.executionGroup,
   475  			}, {
   476  				Receiver:       "mysql/leader",
   477  				Name:           expectedName,
   478  				Parameters:     map[string]interface{}{},
   479  				Parallel:       &t,
   480  				ExecutionGroup: &s.executionGroup,
   481  			},
   482  		}}
   483  
   484  	r, err := api.EnqueueOperation(arg)
   485  	c.Assert(err, jc.ErrorIsNil)
   486  	c.Assert(r.Actions, gc.HasLen, len(arg.Actions))
   487  	c.Assert(r.Actions[0].Status, gc.Equals, "running")
   488  	c.Assert(r.Actions[0].Action.Name, gc.Equals, expectedName)
   489  	c.Assert(r.Actions[0].Action.Tag, gc.Equals, "action-2")
   490  	c.Assert(r.Actions[1].Status, gc.Equals, "running")
   491  	c.Assert(r.Actions[1].Action.Name, gc.Equals, expectedName)
   492  	c.Assert(r.Actions[1].Action.Tag, gc.Equals, "action-3")
   493  }
   494  
   495  func (s *enqueueSuite) setupMocks(c *gc.C) *gomock.Controller {
   496  	ctrl := gomock.NewController(c)
   497  	s.Authorizer = facademocks.NewMockAuthorizer(ctrl)
   498  	s.Authorizer.EXPECT().HasPermission(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
   499  	s.Authorizer.EXPECT().AuthClient().Return(true)
   500  
   501  	s.model = action.NewMockModel(ctrl)
   502  	s.model.EXPECT().ModelTag().Return(s.modelTag).MinTimes(1)
   503  
   504  	s.State = action.NewMockState(ctrl)
   505  	s.State.EXPECT().Model().Return(s.model, nil)
   506  
   507  	s.ActionReceiver = action.NewMockActionReceiver(ctrl)
   508  	s.Leadership = action.NewMockReader(ctrl)
   509  
   510  	s.wordpressAction = action.NewMockAction(ctrl)
   511  	s.mysqlAction = action.NewMockAction(ctrl)
   512  
   513  	return ctrl
   514  }
   515  
   516  func (s *enqueueSuite) expectWordpressActionResult() {
   517  	f := false
   518  	s.model.EXPECT().AddAction(gomock.Any(), "1", "fakeaction", map[string]interface{}{}, &f, &s.executionGroup).Return(s.wordpressAction, nil)
   519  	s.ActionReceiver.EXPECT().Tag().Return(s.wordpressUnitTag)
   520  	aExp := s.wordpressAction.EXPECT()
   521  	aExp.ActionTag().Return(names.NewActionTag("2"))
   522  	aExp.Status().Return(state.ActionRunning)
   523  	aExp.Name().Return("fakeaction")
   524  	aExp.Parameters().Return(map[string]interface{}{})
   525  	aExp.Messages().Return(nil)
   526  	aExp.Results().Return(map[string]interface{}{}, "result")
   527  	aExp.Started().Return(time.Now())
   528  	aExp.Completed().Return(time.Now())
   529  	aExp.Enqueued().Return(time.Now())
   530  	aExp.Parallel().Return(f)
   531  	aExp.ExecutionGroup().Return(s.executionGroup)
   532  }
   533  
   534  func (s *enqueueSuite) expectMysqlActionResult() {
   535  	t := true
   536  	s.model.EXPECT().AddAction(gomock.Any(), "1", "fakeaction", map[string]interface{}{}, &t, &s.executionGroup).Return(s.mysqlAction, nil)
   537  	s.ActionReceiver.EXPECT().Tag().Return(s.mysqlUnitTag)
   538  	aExp := s.mysqlAction.EXPECT()
   539  	aExp.ActionTag().Return(names.NewActionTag("3"))
   540  	aExp.Status().Return(state.ActionRunning)
   541  	aExp.Name().Return("fakeaction")
   542  	aExp.Parameters().Return(map[string]interface{}{})
   543  	aExp.Messages().Return(nil)
   544  	aExp.Results().Return(map[string]interface{}{}, "result")
   545  	aExp.Started().Return(time.Now())
   546  	aExp.Completed().Return(time.Now())
   547  	aExp.Enqueued().Return(time.Now())
   548  	aExp.Parallel().Return(t)
   549  	aExp.ExecutionGroup().Return(s.executionGroup)
   550  }