github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/operation_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names/v5"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/state"
    17  	coretesting "github.com/juju/juju/testing"
    18  )
    19  
    20  type OperationSuite struct {
    21  	ConnSuite
    22  }
    23  
    24  var _ = gc.Suite(&OperationSuite{})
    25  
    26  func (s *OperationSuite) TestEnqueueOperation(c *gc.C) {
    27  	clock := testclock.NewClock(coretesting.NonZeroTime().Round(time.Second))
    28  	err := s.State.SetClockForTesting(clock)
    29  	c.Assert(err, jc.ErrorIsNil)
    30  
    31  	operationID, err := s.Model.EnqueueOperation("an operation", 1)
    32  	c.Assert(err, jc.ErrorIsNil)
    33  
    34  	operation, err := s.Model.Operation(operationID)
    35  	c.Assert(err, jc.ErrorIsNil)
    36  	c.Assert(operation.Id(), gc.Equals, operationID)
    37  	c.Assert(operation.Tag(), gc.Equals, names.NewOperationTag(operationID))
    38  	c.Assert(operation.Status(), gc.Equals, state.ActionPending)
    39  	c.Assert(operation.Enqueued(), gc.Equals, clock.Now())
    40  	c.Assert(operation.Started(), gc.Equals, time.Time{})
    41  	c.Assert(operation.Completed(), gc.Equals, time.Time{})
    42  	c.Assert(operation.Summary(), gc.Equals, "an operation")
    43  }
    44  
    45  func (s *OperationSuite) TestFailOperationEnqueuing(c *gc.C) {
    46  	operationID, err := s.Model.EnqueueOperation("an operation", 5)
    47  	c.Assert(err, jc.ErrorIsNil)
    48  
    49  	err = s.Model.FailOperationEnqueuing(operationID, "fail", 4)
    50  	c.Assert(err, jc.ErrorIsNil)
    51  	operation, err := s.Model.Operation(operationID)
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	c.Assert(operation.Status(), gc.Not(gc.Equals), state.ActionError)
    54  	c.Assert(operation.Fail(), gc.Equals, "fail")
    55  	c.Assert(operation.SpawnedTaskCount(), gc.Equals, 4)
    56  }
    57  
    58  func (s *OperationSuite) TestAllOperations(c *gc.C) {
    59  	operationID, err := s.Model.EnqueueOperation("an operation", 1)
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	operationId2, err := s.Model.EnqueueOperation("another operation", 1)
    62  	c.Assert(err, jc.ErrorIsNil)
    63  
    64  	operations, err := s.Model.AllOperations()
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	c.Assert(operations, gc.HasLen, 2)
    67  
    68  	var ids []string
    69  	for _, op := range operations {
    70  		ids = append(ids, op.Id())
    71  	}
    72  	c.Assert(ids, jc.SameContents, []string{operationID, operationId2})
    73  }
    74  
    75  func (s *OperationSuite) TestOperationStatus(c *gc.C) {
    76  	clock := testclock.NewClock(coretesting.NonZeroTime().Round(time.Second))
    77  	err := s.State.SetClockForTesting(clock)
    78  	c.Assert(err, jc.ErrorIsNil)
    79  
    80  	charm := s.AddTestingCharm(c, "dummy")
    81  	application := s.AddTestingApplication(c, "dummy", charm)
    82  	unit, err := application.AddUnit(state.AddUnitParams{})
    83  	c.Assert(err, jc.ErrorIsNil)
    84  
    85  	operationID, err := s.Model.EnqueueOperation("an operation", 1)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	clock.Advance(5 * time.Second)
    88  	anAction, err := s.Model.EnqueueAction(operationID, unit.Tag(), "backup", nil, false, "", nil)
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	_, err = anAction.Begin()
    91  	c.Assert(err, jc.ErrorIsNil)
    92  
    93  	operation, err := s.Model.Operation(operationID)
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	c.Assert(operation.Status(), gc.Equals, state.ActionRunning)
    96  	c.Assert(operation.Started(), gc.Equals, clock.Now())
    97  	c.Assert(operation.Completed(), gc.Equals, time.Time{})
    98  }
    99  
   100  func (s *OperationSuite) TestRefresh(c *gc.C) {
   101  	charm := s.AddTestingCharm(c, "dummy")
   102  	application := s.AddTestingApplication(c, "dummy", charm)
   103  	unit, err := application.AddUnit(state.AddUnitParams{})
   104  	c.Assert(err, jc.ErrorIsNil)
   105  
   106  	operationID, err := s.Model.EnqueueOperation("an operation", 1)
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	operation, err := s.Model.Operation(operationID)
   109  	c.Assert(err, jc.ErrorIsNil)
   110  
   111  	anAction, err := s.Model.EnqueueAction(operationID, unit.Tag(), "backup", nil, false, "", nil)
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	_, err = anAction.Begin()
   114  	c.Assert(err, jc.ErrorIsNil)
   115  
   116  	err = operation.Refresh()
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	c.Assert(operation.Status(), gc.Equals, state.ActionRunning)
   119  }
   120  
   121  func (s *OperationSuite) setupOperations(c *gc.C) names.Tag {
   122  	clock := testclock.NewClock(coretesting.NonZeroTime().Round(time.Second))
   123  	err := s.State.SetClockForTesting(clock)
   124  	c.Assert(err, jc.ErrorIsNil)
   125  
   126  	charm := s.AddTestingCharm(c, "dummy")
   127  	application := s.AddTestingApplication(c, "dummy", charm)
   128  	unit, err := application.AddUnit(state.AddUnitParams{})
   129  	c.Assert(err, jc.ErrorIsNil)
   130  
   131  	operationID, err := s.Model.EnqueueOperation("an operation", 1)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	operationID2, err := s.Model.EnqueueOperation("another operation", 1)
   134  	c.Assert(err, jc.ErrorIsNil)
   135  
   136  	clock.Advance(5 * time.Second)
   137  	anAction, err := s.Model.EnqueueAction(operationID, unit.Tag(), "backup", nil, false, "", nil)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	_, err = anAction.Begin()
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	anAction2, err := s.Model.EnqueueAction(operationID2, unit.Tag(), "restore", nil, false, "", nil)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	a, err := anAction2.Begin()
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	err = a.Log("hello")
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	_, err = anAction2.Finish(state.ActionResults{
   148  		Status:  state.ActionCompleted,
   149  		Message: "done",
   150  		Results: map[string]interface{}{"foo": "bar"},
   151  	})
   152  	c.Assert(err, jc.ErrorIsNil)
   153  
   154  	unit2, err := application.AddUnit(state.AddUnitParams{})
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	operationID3, err := s.Model.EnqueueOperation("yet another operation", 1)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	anAction3, err := s.Model.EnqueueAction(operationID3, unit2.Tag(), "backup", nil, false, "", nil)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	_, err = anAction3.Begin()
   161  	c.Assert(err, jc.ErrorIsNil)
   162  
   163  	return unit.Tag()
   164  }
   165  
   166  func (s *OperationSuite) assertActions(c *gc.C, operations []state.OperationInfo) {
   167  	for _, operation := range operations {
   168  		for _, a := range operation.Actions {
   169  			c.Assert(operation.Operation.Id(), gc.Equals, state.ActionOperationId(a))
   170  			if a.Name() == "restore" {
   171  				c.Assert(a.Status(), gc.Equals, state.ActionCompleted)
   172  			} else {
   173  				c.Assert(a.Status(), gc.Equals, state.ActionRunning)
   174  			}
   175  			c.Assert(a.Messages(), gc.HasLen, 0)
   176  			c.Assert(a.Messages(), gc.HasLen, 0)
   177  			results, _ := a.Results()
   178  			c.Assert(results, gc.HasLen, 0)
   179  		}
   180  	}
   181  }
   182  
   183  func (s *OperationSuite) TestListOperationsNoFilter(c *gc.C) {
   184  	s.setupOperations(c)
   185  	operations, truncated, err := s.Model.ListOperations(nil, nil, nil, 0, 0)
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(truncated, jc.IsFalse)
   188  	c.Assert(operations, gc.HasLen, 3)
   189  	c.Assert(operations[0].Operation.Summary(), gc.Equals, "an operation")
   190  	c.Assert(operations[0].Actions, gc.HasLen, 1)
   191  	c.Assert(operations[1].Operation.Summary(), gc.Equals, "another operation")
   192  	c.Assert(operations[1].Actions, gc.HasLen, 1)
   193  	c.Assert(operations[2].Operation.Summary(), gc.Equals, "yet another operation")
   194  	c.Assert(operations[2].Actions, gc.HasLen, 1)
   195  	s.assertActions(c, operations)
   196  }
   197  
   198  func (s *OperationSuite) TestListOperations(c *gc.C) {
   199  	unitTag := s.setupOperations(c)
   200  	operations, truncated, err := s.Model.ListOperations([]string{"backup"}, []names.Tag{unitTag}, []state.ActionStatus{state.ActionRunning}, 0, 0)
   201  	c.Assert(truncated, jc.IsFalse)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	c.Assert(operations, gc.HasLen, 1)
   204  	c.Assert(operations[0].Operation.Summary(), gc.Equals, "an operation")
   205  	c.Assert(operations[0].Actions, gc.HasLen, 1)
   206  	s.assertActions(c, operations)
   207  }
   208  
   209  func (s *OperationSuite) TestListOperationsByStatus(c *gc.C) {
   210  	s.setupOperations(c)
   211  	operations, truncated, err := s.Model.ListOperations(nil, nil, []state.ActionStatus{state.ActionCompleted}, 0, 0)
   212  	c.Assert(truncated, jc.IsFalse)
   213  	c.Assert(err, jc.ErrorIsNil)
   214  	c.Assert(operations, gc.HasLen, 1)
   215  	c.Assert(operations[0].Operation.Summary(), gc.Equals, "another operation")
   216  	c.Assert(operations[0].Actions, gc.HasLen, 1)
   217  	s.assertActions(c, operations)
   218  }
   219  
   220  func (s *OperationSuite) TestListOperationsByName(c *gc.C) {
   221  	s.setupOperations(c)
   222  	operations, truncated, err := s.Model.ListOperations([]string{"restore"}, nil, nil, 0, 0)
   223  	c.Assert(truncated, jc.IsFalse)
   224  	c.Assert(err, jc.ErrorIsNil)
   225  	c.Assert(operations, gc.HasLen, 1)
   226  	c.Assert(operations[0].Operation.Summary(), gc.Equals, "another operation")
   227  	c.Assert(operations[0].Actions, gc.HasLen, 1)
   228  	s.assertActions(c, operations)
   229  }
   230  
   231  func (s *OperationSuite) TestListOperationsByReceiver(c *gc.C) {
   232  	unitTag := s.setupOperations(c)
   233  	operations, truncated, err := s.Model.ListOperations(nil, []names.Tag{unitTag}, nil, 0, 0)
   234  	c.Assert(truncated, jc.IsFalse)
   235  	c.Assert(err, jc.ErrorIsNil)
   236  	c.Assert(operations, gc.HasLen, 2)
   237  	c.Assert(operations[0].Operation.Summary(), gc.Equals, "an operation")
   238  	c.Assert(operations[0].Actions, gc.HasLen, 1)
   239  	c.Assert(operations[1].Operation.Summary(), gc.Equals, "another operation")
   240  	c.Assert(operations[1].Actions, gc.HasLen, 1)
   241  	s.assertActions(c, operations)
   242  }
   243  
   244  func (s *OperationSuite) TestListOperationsSubset(c *gc.C) {
   245  	s.setupOperations(c)
   246  	for i := 0; i < 20; i++ {
   247  		operationID, err := s.Model.EnqueueOperation(fmt.Sprintf("operation %d", i), 20)
   248  		c.Assert(err, jc.ErrorIsNil)
   249  		anAction, err := s.Model.EnqueueAction(operationID, names.NewUnitTag("dummy/0"), "backup", nil, false, "", nil)
   250  		c.Assert(err, jc.ErrorIsNil)
   251  		_, err = anAction.Begin()
   252  		c.Assert(err, jc.ErrorIsNil)
   253  	}
   254  	operations, truncated, err := s.Model.ListOperations(nil, nil, nil, 14, 1)
   255  	c.Assert(truncated, jc.IsTrue)
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	c.Assert(operations, gc.HasLen, 1)
   258  	c.Assert(operations[0].Operation.Summary(), gc.Equals, "operation 11")
   259  	c.Assert(operations[0].Actions, gc.HasLen, 1)
   260  	s.assertActions(c, operations)
   261  }
   262  
   263  func (s *OperationSuite) TestOperationWithActions(c *gc.C) {
   264  	s.setupOperations(c)
   265  	operation, err := s.Model.OperationWithActions("2")
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	c.Assert(operation.Operation.OperationTag().String(), gc.Equals, "operation-2")
   268  	c.Assert(operation.Operation.Id(), gc.Equals, "2")
   269  	c.Assert(operation.Operation.Summary(), gc.Equals, "another operation")
   270  	c.Assert(operation.Operation.Status(), gc.Equals, state.ActionCompleted)
   271  	c.Assert(operation.Operation.Enqueued(), gc.Not(gc.Equals), time.Time{})
   272  	c.Assert(operation.Operation.Started(), gc.Not(gc.Equals), time.Time{})
   273  	c.Assert(operation.Operation.Completed(), gc.Not(gc.Equals), time.Time{})
   274  	c.Assert(operation.Actions, gc.HasLen, 1)
   275  	c.Assert(operation.Actions[0].Id(), gc.Equals, "4")
   276  	c.Assert(operation.Actions[0].Status(), gc.Equals, state.ActionCompleted)
   277  	results, message := operation.Actions[0].Results()
   278  	c.Assert(results, jc.DeepEquals, map[string]interface{}{"foo": "bar"})
   279  	c.Assert(message, gc.Equals, "done")
   280  	c.Assert(operation.Actions[0].Messages(), gc.HasLen, 1)
   281  	c.Assert(operation.Actions[0].Messages()[0].Message(), gc.Equals, "hello")
   282  }
   283  
   284  func (s *OperationSuite) TestOperationWithActionsNotFound(c *gc.C) {
   285  	_, err := s.Model.OperationWithActions("1")
   286  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   287  }