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 }