github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/action/action_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package action_test 5 6 import ( 7 "fmt" 8 "testing" 9 "time" 10 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/names.v2" 15 16 "github.com/juju/juju/apiserver/common" 17 commontesting "github.com/juju/juju/apiserver/common/testing" 18 "github.com/juju/juju/apiserver/facades/client/action" 19 "github.com/juju/juju/apiserver/params" 20 apiservertesting "github.com/juju/juju/apiserver/testing" 21 jujutesting "github.com/juju/juju/juju/testing" 22 "github.com/juju/juju/state" 23 coretesting "github.com/juju/juju/testing" 24 "github.com/juju/juju/testing/factory" 25 ) 26 27 func TestAll(t *testing.T) { 28 coretesting.MgoTestPackage(t) 29 } 30 31 type actionSuite struct { 32 jujutesting.JujuConnSuite 33 commontesting.BlockHelper 34 35 action *action.ActionAPI 36 authorizer apiservertesting.FakeAuthorizer 37 resources *common.Resources 38 39 charm *state.Charm 40 machine0 *state.Machine 41 machine1 *state.Machine 42 dummy *state.Application 43 wordpress *state.Application 44 mysql *state.Application 45 wordpressUnit *state.Unit 46 mysqlUnit *state.Unit 47 } 48 49 var _ = gc.Suite(&actionSuite{}) 50 51 func (s *actionSuite) SetUpTest(c *gc.C) { 52 s.JujuConnSuite.SetUpTest(c) 53 s.BlockHelper = commontesting.NewBlockHelper(s.APIState) 54 s.AddCleanup(func(*gc.C) { s.BlockHelper.Close() }) 55 56 s.authorizer = apiservertesting.FakeAuthorizer{ 57 Tag: s.AdminUserTag(c), 58 } 59 var err error 60 s.action, err = action.NewActionAPI(s.State, nil, s.authorizer) 61 c.Assert(err, jc.ErrorIsNil) 62 63 s.charm = s.Factory.MakeCharm(c, &factory.CharmParams{ 64 Name: "wordpress", 65 }) 66 67 s.dummy = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 68 Name: "dummy", 69 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 70 Name: "dummy", 71 }), 72 }) 73 s.wordpress = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 74 Name: "wordpress", 75 Charm: s.charm, 76 }) 77 s.machine0 = s.Factory.MakeMachine(c, &factory.MachineParams{ 78 Series: "quantal", 79 Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageModel}, 80 }) 81 s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 82 Application: s.wordpress, 83 Machine: s.machine0, 84 }) 85 86 mysqlCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 87 Name: "mysql", 88 }) 89 s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 90 Name: "mysql", 91 Charm: mysqlCharm, 92 }) 93 s.machine1 = s.Factory.MakeMachine(c, &factory.MachineParams{ 94 Series: "quantal", 95 Jobs: []state.MachineJob{state.JobHostUnits}, 96 }) 97 s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 98 Application: s.mysql, 99 Machine: s.machine1, 100 }) 101 s.resources = common.NewResources() 102 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 103 } 104 105 func (s *actionSuite) AssertBlocked(c *gc.C, err error, msg string) { 106 c.Assert(params.IsCodeOperationBlocked(err), jc.IsTrue, gc.Commentf("error: %#v", err)) 107 c.Assert(errors.Cause(err), gc.DeepEquals, ¶ms.Error{ 108 Message: msg, 109 Code: "operation is blocked", 110 }) 111 } 112 113 func (s *actionSuite) TestBlockEnqueue(c *gc.C) { 114 // block all changes 115 s.BlockAllChanges(c, "Enqueue") 116 _, err := s.action.Enqueue(params.Actions{}) 117 s.AssertBlocked(c, err, "Enqueue") 118 } 119 120 func (s *actionSuite) TestBlockCancel(c *gc.C) { 121 // block all changes 122 s.BlockAllChanges(c, "Cancel") 123 _, err := s.action.Cancel(params.Entities{}) 124 s.AssertBlocked(c, err, "Cancel") 125 } 126 127 func (s *actionSuite) TestActions(c *gc.C) { 128 arg := params.Actions{ 129 Actions: []params.Action{ 130 {Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}}, 131 {Receiver: s.mysqlUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}}, 132 {Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{"foo": 1, "bar": "please"}}, 133 {Receiver: s.mysqlUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{"baz": true}}, 134 }} 135 136 r, err := s.action.Enqueue(arg) 137 c.Assert(err, gc.Equals, nil) 138 c.Assert(r.Results, gc.HasLen, len(arg.Actions)) 139 140 entities := make([]params.Entity, len(r.Results)) 141 for i, result := range r.Results { 142 entities[i] = params.Entity{Tag: result.Action.Tag} 143 } 144 145 actions, err := s.action.Actions(params.Entities{Entities: entities}) 146 c.Assert(err, gc.Equals, nil) 147 148 c.Assert(len(actions.Results), gc.Equals, len(entities)) 149 for i, got := range actions.Results { 150 c.Logf("check index %d (%s: %s)", i, entities[i].Tag, arg.Actions[i].Name) 151 c.Assert(got.Error, gc.Equals, (*params.Error)(nil)) 152 c.Assert(got.Action, gc.Not(gc.Equals), (*params.Action)(nil)) 153 c.Assert(got.Action.Tag, gc.Equals, entities[i].Tag) 154 c.Assert(got.Action.Name, gc.Equals, arg.Actions[i].Name) 155 c.Assert(got.Action.Receiver, gc.Equals, arg.Actions[i].Receiver) 156 c.Assert(got.Action.Parameters, gc.DeepEquals, arg.Actions[i].Parameters) 157 c.Assert(got.Status, gc.Equals, params.ActionPending) 158 c.Assert(got.Message, gc.Equals, "") 159 c.Assert(got.Output, gc.DeepEquals, map[string]interface{}{}) 160 } 161 } 162 163 func (s *actionSuite) TestFindActionTagsByPrefix(c *gc.C) { 164 // NOTE: full testing with multiple matches has been moved to state package. 165 arg := params.Actions{Actions: []params.Action{{Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}}}} 166 r, err := s.action.Enqueue(arg) 167 c.Assert(err, gc.Equals, nil) 168 c.Assert(r.Results, gc.HasLen, len(arg.Actions)) 169 170 actionTag, err := names.ParseActionTag(r.Results[0].Action.Tag) 171 c.Assert(err, gc.Equals, nil) 172 prefix := actionTag.Id()[:7] 173 tags, err := s.action.FindActionTagsByPrefix(params.FindTags{Prefixes: []string{prefix}}) 174 c.Assert(err, gc.Equals, nil) 175 176 entities, ok := tags.Matches[prefix] 177 c.Assert(ok, gc.Equals, true) 178 c.Assert(len(entities), gc.Equals, 1) 179 c.Assert(entities[0].Tag, gc.Equals, actionTag.String()) 180 } 181 182 func (s *actionSuite) TestFindActionsByName(c *gc.C) { 183 machine := s.JujuConnSuite.Factory.MakeMachine(c, &factory.MachineParams{ 184 Series: "quantal", 185 Jobs: []state.MachineJob{state.JobHostUnits}, 186 }) 187 dummyUnit := s.JujuConnSuite.Factory.MakeUnit(c, &factory.UnitParams{ 188 Application: s.dummy, 189 Machine: machine, 190 }) 191 // NOTE: full testing with multiple matches has been moved to state package. 192 arg := params.Actions{Actions: []params.Action{ 193 {Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}}, 194 {Receiver: dummyUnit.Tag().String(), Name: "snapshot", Parameters: map[string]interface{}{"outfile": "lol"}}, 195 {Receiver: s.wordpressUnit.Tag().String(), Name: "juju-run", Parameters: map[string]interface{}{"command": "boo", "timeout": 5}}, 196 {Receiver: s.mysqlUnit.Tag().String(), Name: "juju-run", Parameters: map[string]interface{}{"command": "boo", "timeout": 5}}, 197 }} 198 r, err := s.action.Enqueue(arg) 199 c.Assert(err, gc.Equals, nil) 200 c.Assert(r.Results, gc.HasLen, len(arg.Actions)) 201 202 actionNames := []string{"snapshot", "juju-run"} 203 actions, err := s.action.FindActionsByNames(params.FindActionsByNames{ActionNames: actionNames}) 204 c.Assert(err, jc.ErrorIsNil) 205 206 c.Assert(len(actions.Actions), gc.Equals, 2) 207 for i, actions := range actions.Actions { 208 for _, act := range actions.Actions { 209 c.Assert(act.Action.Name, gc.Equals, actionNames[i]) 210 c.Assert(act.Action.Name, gc.Matches, actions.Name) 211 } 212 } 213 } 214 215 func (s *actionSuite) TestEnqueue(c *gc.C) { 216 // Ensure wordpress unit is the leader. 217 claimer, err := s.LeaseManager.Claimer("application-leadership", s.State.ModelUUID()) 218 c.Assert(err, jc.ErrorIsNil) 219 err = claimer.Claim("wordpress", "wordpress/0", time.Minute) 220 c.Assert(err, jc.ErrorIsNil) 221 222 // Make sure no Actions already exist on wordpress Unit. 223 actions, err := s.wordpressUnit.Actions() 224 c.Assert(err, jc.ErrorIsNil) 225 c.Assert(actions, gc.HasLen, 0) 226 227 // Make sure no Actions already exist on mysql Unit. 228 actions, err = s.mysqlUnit.Actions() 229 c.Assert(err, jc.ErrorIsNil) 230 c.Assert(actions, gc.HasLen, 0) 231 232 // Add Actions. 233 expectedName := "fakeaction" 234 expectedParameters := map[string]interface{}{"kan jy nie": "verstaand"} 235 arg := params.Actions{ 236 Actions: []params.Action{ 237 // No receiver. 238 {Name: "fakeaction"}, 239 // Good. 240 {Receiver: s.wordpressUnit.Tag().String(), Name: expectedName, Parameters: expectedParameters}, 241 // Application tag instead of Unit tag. 242 {Receiver: s.wordpress.Tag().String(), Name: "fakeaction"}, 243 // Missing name. 244 {Receiver: s.mysqlUnit.Tag().String(), Parameters: expectedParameters}, 245 // Good (leader syntax). 246 {Receiver: "wordpress/leader", Name: expectedName, Parameters: expectedParameters}, 247 }, 248 } 249 res, err := s.action.Enqueue(arg) 250 c.Assert(err, jc.ErrorIsNil) 251 c.Assert(res.Results, gc.HasLen, 5) 252 253 emptyActionTag := names.ActionTag{} 254 c.Assert(res.Results[0].Error, gc.DeepEquals, 255 ¶ms.Error{Message: fmt.Sprintf("%s not valid", arg.Actions[0].Receiver), Code: ""}) 256 c.Assert(res.Results[0].Action, gc.IsNil) 257 258 c.Assert(res.Results[1].Error, gc.IsNil) 259 c.Assert(res.Results[1].Action, gc.NotNil) 260 c.Assert(res.Results[1].Action.Receiver, gc.Equals, s.wordpressUnit.Tag().String()) 261 c.Assert(res.Results[1].Action.Tag, gc.Not(gc.Equals), emptyActionTag) 262 263 errorString := fmt.Sprintf("action receiver interface on entity %s not implemented", arg.Actions[2].Receiver) 264 c.Assert(res.Results[2].Error, gc.DeepEquals, ¶ms.Error{Message: errorString, Code: "not implemented"}) 265 c.Assert(res.Results[2].Action, gc.IsNil) 266 267 c.Assert(res.Results[3].Error, gc.ErrorMatches, "no action name given") 268 c.Assert(res.Results[3].Action, gc.IsNil) 269 270 c.Assert(res.Results[4].Error, gc.IsNil) 271 c.Assert(res.Results[4].Action, gc.NotNil) 272 c.Assert(res.Results[4].Action.Receiver, gc.Equals, s.wordpressUnit.Tag().String()) 273 c.Assert(res.Results[4].Action.Tag, gc.Not(gc.Equals), emptyActionTag) 274 275 // Make sure that 2 actions were enqueued for the wordpress Unit. 276 actions, err = s.wordpressUnit.Actions() 277 c.Assert(err, jc.ErrorIsNil) 278 c.Assert(actions, gc.HasLen, 2) 279 for _, act := range actions { 280 c.Assert(act.Name(), gc.Equals, expectedName) 281 c.Assert(act.Parameters(), gc.DeepEquals, expectedParameters) 282 c.Assert(act.Receiver(), gc.Equals, s.wordpressUnit.Name()) 283 } 284 285 // Make sure an Action was not enqueued for the mysql Unit. 286 actions, err = s.mysqlUnit.Actions() 287 c.Assert(err, jc.ErrorIsNil) 288 c.Assert(actions, gc.HasLen, 0) 289 } 290 291 type testCaseAction struct { 292 Name string 293 Parameters map[string]interface{} 294 Execute bool 295 } 296 297 type receiverGroup struct { 298 ExpectedError *params.Error 299 Receiver names.Tag 300 Actions []testCaseAction 301 } 302 303 type testCase struct { 304 Groups []receiverGroup 305 } 306 307 var testCases = []testCase{{ 308 Groups: []receiverGroup{ 309 { 310 ExpectedError: ¶ms.Error{Message: "id not found", Code: "not found"}, 311 Receiver: names.NewApplicationTag("wordpress"), 312 Actions: []testCaseAction{}, 313 }, { 314 Receiver: names.NewUnitTag("wordpress/0"), 315 Actions: []testCaseAction{ 316 {"fakeaction", map[string]interface{}{}, false}, 317 {"fakeaction", map[string]interface{}{"asdf": 3}, true}, 318 {"fakeaction", map[string]interface{}{"qwer": "ty"}, false}, 319 }, 320 }, { 321 Receiver: names.NewUnitTag("mysql/0"), 322 Actions: []testCaseAction{ 323 {"fakeaction", map[string]interface{}{"zxcv": false}, false}, 324 {"fakeaction", map[string]interface{}{}, true}, 325 }, 326 }, 327 }, 328 }} 329 330 func (s *actionSuite) TestListAll(c *gc.C) { 331 for _, testCase := range testCases { 332 // set up query args 333 arg := params.Entities{Entities: make([]params.Entity, len(testCase.Groups))} 334 335 // prepare state, and set up expectations. 336 expected := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(testCase.Groups))} 337 for i, group := range testCase.Groups { 338 arg.Entities[i] = params.Entity{Tag: group.Receiver.String()} 339 340 cur := &expected.Actions[i] 341 cur.Error = group.ExpectedError 342 343 // short circuit and bail if the ActionReceiver isn't a Unit. 344 if _, ok := group.Receiver.(names.UnitTag); !ok { 345 continue 346 } 347 348 cur.Receiver = group.Receiver.String() 349 cur.Actions = make([]params.ActionResult, len(group.Actions)) 350 351 // get Unit (ActionReceiver) for this Pair in the test case. 352 unit, err := s.State.Unit(group.Receiver.Id()) 353 c.Assert(err, jc.ErrorIsNil) 354 assertReadyToTest(c, unit) 355 356 // add each action from the test case. 357 for j, act := range group.Actions { 358 // add action. 359 added, err := unit.AddAction(act.Name, act.Parameters) 360 c.Assert(err, jc.ErrorIsNil) 361 362 // make expectation 363 exp := &cur.Actions[j] 364 exp.Action = ¶ms.Action{ 365 Tag: added.ActionTag().String(), 366 Name: act.Name, 367 Parameters: act.Parameters, 368 } 369 exp.Status = params.ActionPending 370 exp.Output = map[string]interface{}{} 371 372 if act.Execute { 373 status := state.ActionCompleted 374 output := map[string]interface{}{"output": "blah, blah, blah"} 375 message := "success" 376 377 fa, err := added.Finish(state.ActionResults{Status: status, Results: output, Message: message}) 378 c.Assert(err, jc.ErrorIsNil) 379 c.Assert(fa.Status(), gc.Equals, state.ActionCompleted) 380 381 exp.Status = string(status) 382 exp.Message = message 383 exp.Output = output 384 } 385 } 386 } 387 388 // validate assumptions. 389 actionList, err := s.action.ListAll(arg) 390 c.Assert(err, jc.ErrorIsNil) 391 assertSame(c, actionList, expected) 392 } 393 } 394 395 func (s *actionSuite) TestListPending(c *gc.C) { 396 for _, testCase := range testCases { 397 // set up query args 398 arg := params.Entities{Entities: make([]params.Entity, len(testCase.Groups))} 399 400 // prepare state, and set up expectations. 401 expected := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(testCase.Groups))} 402 for i, group := range testCase.Groups { 403 arg.Entities[i] = params.Entity{Tag: group.Receiver.String()} 404 405 cur := &expected.Actions[i] 406 cur.Error = group.ExpectedError 407 408 // short circuit and bail if the ActionReceiver isn't a Unit. 409 if _, ok := group.Receiver.(names.UnitTag); !ok { 410 continue 411 } 412 413 cur.Receiver = group.Receiver.String() 414 cur.Actions = []params.ActionResult{} 415 416 // get Unit (ActionReceiver) for this Pair in the test case. 417 unit, err := s.State.Unit(group.Receiver.Id()) 418 c.Assert(err, jc.ErrorIsNil) 419 assertReadyToTest(c, unit) 420 421 // add each action from the test case. 422 for _, act := range group.Actions { 423 // add action. 424 added, err := unit.AddAction(act.Name, act.Parameters) 425 c.Assert(err, jc.ErrorIsNil) 426 427 if act.Execute { 428 status := state.ActionCompleted 429 output := map[string]interface{}{"output": "blah, blah, blah"} 430 message := "success" 431 432 fa, err := added.Finish(state.ActionResults{Status: status, Results: output, Message: message}) 433 c.Assert(err, jc.ErrorIsNil) 434 c.Assert(fa.Status(), gc.Equals, state.ActionCompleted) 435 } else { 436 // add expectation 437 exp := params.ActionResult{ 438 Action: ¶ms.Action{ 439 Tag: added.ActionTag().String(), 440 Name: act.Name, 441 Parameters: act.Parameters, 442 }, 443 Status: params.ActionPending, 444 Output: map[string]interface{}{}, 445 } 446 cur.Actions = append(cur.Actions, exp) 447 } 448 } 449 } 450 451 // validate assumptions. 452 actionList, err := s.action.ListPending(arg) 453 c.Assert(err, jc.ErrorIsNil) 454 assertSame(c, actionList, expected) 455 } 456 } 457 458 func (s *actionSuite) TestListRunning(c *gc.C) { 459 for _, testCase := range testCases { 460 // set up query args 461 arg := params.Entities{Entities: make([]params.Entity, len(testCase.Groups))} 462 463 // prepare state, and set up expectations. 464 expected := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(testCase.Groups))} 465 for i, group := range testCase.Groups { 466 arg.Entities[i] = params.Entity{Tag: group.Receiver.String()} 467 468 cur := &expected.Actions[i] 469 cur.Error = group.ExpectedError 470 471 // short circuit and bail if the ActionReceiver isn't a Unit. 472 if _, ok := group.Receiver.(names.UnitTag); !ok { 473 continue 474 } 475 476 cur.Receiver = group.Receiver.String() 477 cur.Actions = []params.ActionResult{} 478 479 // get Unit (ActionReceiver) for this Pair in the test case. 480 unit, err := s.State.Unit(group.Receiver.Id()) 481 c.Assert(err, jc.ErrorIsNil) 482 assertReadyToTest(c, unit) 483 484 // add each action from the test case. 485 for _, act := range group.Actions { 486 // add action. 487 added, err := unit.AddAction(act.Name, act.Parameters) 488 c.Assert(err, jc.ErrorIsNil) 489 490 if act.Execute { 491 started, err := added.Begin() 492 c.Assert(err, jc.ErrorIsNil) 493 c.Assert(started.Status(), gc.Equals, state.ActionRunning) 494 495 // add expectation 496 exp := params.ActionResult{ 497 Action: ¶ms.Action{ 498 Tag: added.ActionTag().String(), 499 Name: act.Name, 500 Parameters: act.Parameters, 501 }, 502 Status: params.ActionRunning, 503 Output: map[string]interface{}{}, 504 } 505 cur.Actions = append(cur.Actions, exp) 506 } 507 } 508 } 509 510 // validate assumptions. 511 actionList, err := s.action.ListRunning(arg) 512 c.Assert(err, jc.ErrorIsNil) 513 assertSame(c, actionList, expected) 514 } 515 } 516 517 func (s *actionSuite) TestListCompleted(c *gc.C) { 518 for _, testCase := range testCases { 519 // set up query args 520 arg := params.Entities{Entities: make([]params.Entity, len(testCase.Groups))} 521 522 // prepare state, and set up expectations. 523 expected := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(testCase.Groups))} 524 for i, group := range testCase.Groups { 525 arg.Entities[i] = params.Entity{Tag: group.Receiver.String()} 526 527 cur := &expected.Actions[i] 528 cur.Error = group.ExpectedError 529 530 // short circuit and bail if the ActionReceiver isn't a Unit. 531 if _, ok := group.Receiver.(names.UnitTag); !ok { 532 continue 533 } 534 535 cur.Receiver = group.Receiver.String() 536 cur.Actions = []params.ActionResult{} 537 538 // get Unit (ActionReceiver) for this Pair in the test case. 539 unit, err := s.State.Unit(group.Receiver.Id()) 540 c.Assert(err, jc.ErrorIsNil) 541 assertReadyToTest(c, unit) 542 543 // add each action from the test case. 544 for _, act := range group.Actions { 545 // add action. 546 added, err := unit.AddAction(act.Name, act.Parameters) 547 c.Assert(err, jc.ErrorIsNil) 548 549 if act.Execute { 550 status := state.ActionCompleted 551 output := map[string]interface{}{"output": "blah, blah, blah"} 552 message := "success" 553 554 _, err = added.Finish(state.ActionResults{Status: status, Results: output, Message: message}) 555 c.Assert(err, jc.ErrorIsNil) 556 557 // add expectation 558 exp := params.ActionResult{ 559 Action: ¶ms.Action{ 560 Tag: added.ActionTag().String(), 561 Name: act.Name, 562 Parameters: act.Parameters, 563 }, 564 Status: string(status), 565 Message: message, 566 Output: output, 567 } 568 cur.Actions = append(cur.Actions, exp) 569 } 570 } 571 } 572 573 // validate assumptions. 574 actionList, err := s.action.ListCompleted(arg) 575 c.Assert(err, jc.ErrorIsNil) 576 assertSame(c, actionList, expected) 577 } 578 } 579 580 func (s *actionSuite) TestCancel(c *gc.C) { 581 // Make sure no Actions already exist on wordpress Unit. 582 actions, err := s.wordpressUnit.Actions() 583 c.Assert(err, jc.ErrorIsNil) 584 c.Assert(actions, gc.HasLen, 0) 585 586 // Make sure no Actions already exist on mysql Unit. 587 actions, err = s.mysqlUnit.Actions() 588 c.Assert(err, jc.ErrorIsNil) 589 c.Assert(actions, gc.HasLen, 0) 590 591 // Add Actions. 592 tests := params.Actions{ 593 Actions: []params.Action{{ 594 Receiver: s.wordpressUnit.Tag().String(), 595 Name: "fakeaction", 596 }, { 597 Receiver: s.wordpressUnit.Tag().String(), 598 Name: "fakeaction", 599 }, { 600 Receiver: s.mysqlUnit.Tag().String(), 601 Name: "fakeaction", 602 }, { 603 Receiver: s.mysqlUnit.Tag().String(), 604 Name: "fakeaction", 605 }}, 606 } 607 608 results, err := s.action.Enqueue(tests) 609 c.Assert(err, jc.ErrorIsNil) 610 c.Assert(results.Results, gc.HasLen, 4) 611 for _, res := range results.Results { 612 c.Assert(res.Error, gc.IsNil) 613 } 614 615 // Cancel Some. 616 arg := params.Entities{ 617 Entities: []params.Entity{ 618 // "wp-two" 619 {Tag: results.Results[1].Action.Tag}, 620 // "my-one" 621 {Tag: results.Results[2].Action.Tag}, 622 }} 623 results, err = s.action.Cancel(arg) 624 c.Assert(err, jc.ErrorIsNil) 625 c.Assert(results.Results, gc.HasLen, 2) 626 627 // Assert the Actions are all in the expected state. 628 tags := params.Entities{Entities: []params.Entity{ 629 {Tag: s.wordpressUnit.Tag().String()}, 630 {Tag: s.mysqlUnit.Tag().String()}, 631 }} 632 obtained, err := s.action.ListAll(tags) 633 c.Assert(err, jc.ErrorIsNil) 634 c.Assert(obtained.Actions, gc.HasLen, 2) 635 636 wpActions := obtained.Actions[0].Actions 637 c.Assert(wpActions, gc.HasLen, 2) 638 c.Assert(wpActions[0].Action.Name, gc.Equals, "fakeaction") 639 c.Assert(wpActions[0].Status, gc.Equals, params.ActionPending) 640 c.Assert(wpActions[1].Action.Name, gc.Equals, "fakeaction") 641 c.Assert(wpActions[1].Status, gc.Equals, params.ActionCancelled) 642 643 myActions := obtained.Actions[1].Actions 644 c.Assert(myActions, gc.HasLen, 2) 645 c.Assert(myActions[0].Action.Name, gc.Equals, "fakeaction") 646 c.Assert(myActions[0].Status, gc.Equals, params.ActionPending) 647 c.Assert(myActions[1].Action.Name, gc.Equals, "fakeaction") 648 c.Assert(myActions[1].Status, gc.Equals, params.ActionCancelled) 649 } 650 651 func (s *actionSuite) TestApplicationsCharmsActions(c *gc.C) { 652 actionSchemas := map[string]map[string]interface{}{ 653 "snapshot": { 654 "type": "object", 655 "title": "snapshot", 656 "description": "Take a snapshot of the database.", 657 "properties": map[string]interface{}{ 658 "outfile": map[string]interface{}{ 659 "description": "The file to write out to.", 660 "type": "string", 661 "default": "foo.bz2", 662 }, 663 }, 664 }, 665 "fakeaction": { 666 "type": "object", 667 "title": "fakeaction", 668 "description": "No description", 669 "properties": map[string]interface{}{}, 670 }, 671 } 672 tests := []struct { 673 applicationNames []string 674 expectedResults params.ApplicationsCharmActionsResults 675 }{{ 676 applicationNames: []string{"dummy"}, 677 expectedResults: params.ApplicationsCharmActionsResults{ 678 Results: []params.ApplicationCharmActionsResult{ 679 { 680 ApplicationTag: names.NewApplicationTag("dummy").String(), 681 Actions: map[string]params.ActionSpec{ 682 "snapshot": { 683 Description: "Take a snapshot of the database.", 684 Params: actionSchemas["snapshot"], 685 }, 686 }, 687 }, 688 }, 689 }, 690 }, { 691 applicationNames: []string{"wordpress"}, 692 expectedResults: params.ApplicationsCharmActionsResults{ 693 Results: []params.ApplicationCharmActionsResult{ 694 { 695 ApplicationTag: names.NewApplicationTag("wordpress").String(), 696 Actions: map[string]params.ActionSpec{ 697 "fakeaction": { 698 Description: "No description", 699 Params: actionSchemas["fakeaction"], 700 }, 701 }, 702 }, 703 }, 704 }, 705 }, { 706 applicationNames: []string{"nonsense"}, 707 expectedResults: params.ApplicationsCharmActionsResults{ 708 Results: []params.ApplicationCharmActionsResult{ 709 { 710 ApplicationTag: names.NewApplicationTag("nonsense").String(), 711 Error: ¶ms.Error{ 712 Message: `application "nonsense" not found`, 713 Code: "not found", 714 }, 715 }, 716 }, 717 }, 718 }} 719 720 for i, t := range tests { 721 c.Logf("test %d: applications: %#v", i, t.applicationNames) 722 723 svcTags := params.Entities{ 724 Entities: make([]params.Entity, len(t.applicationNames)), 725 } 726 727 for j, app := range t.applicationNames { 728 svcTag := names.NewApplicationTag(app) 729 svcTags.Entities[j] = params.Entity{Tag: svcTag.String()} 730 } 731 732 results, err := s.action.ApplicationsCharmsActions(svcTags) 733 c.Assert(err, jc.ErrorIsNil) 734 c.Check(results.Results, jc.DeepEquals, t.expectedResults.Results) 735 } 736 } 737 738 func assertReadyToTest(c *gc.C, receiver state.ActionReceiver) { 739 // make sure there are no actions on the receiver already. 740 actions, err := receiver.Actions() 741 c.Assert(err, jc.ErrorIsNil) 742 c.Assert(actions, gc.HasLen, 0) 743 744 // make sure there are no actions pending already. 745 actions, err = receiver.PendingActions() 746 c.Assert(err, jc.ErrorIsNil) 747 c.Assert(actions, gc.HasLen, 0) 748 749 // make sure there are no actions running already. 750 actions, err = receiver.RunningActions() 751 c.Assert(err, jc.ErrorIsNil) 752 c.Assert(actions, gc.HasLen, 0) 753 754 // make sure there are no actions completed already. 755 actions, err = receiver.CompletedActions() 756 c.Assert(err, jc.ErrorIsNil) 757 c.Assert(actions, gc.HasLen, 0) 758 } 759 760 func assertSame(c *gc.C, got, expected params.ActionsByReceivers) { 761 c.Assert(got.Actions, gc.HasLen, len(expected.Actions)) 762 for i, g1 := range got.Actions { 763 e1 := expected.Actions[i] 764 c.Assert(g1.Error, gc.DeepEquals, e1.Error) 765 c.Assert(g1.Receiver, gc.DeepEquals, e1.Receiver) 766 c.Assert(toStrings(g1.Actions), jc.SameContents, toStrings(e1.Actions)) 767 } 768 } 769 770 func toStrings(items []params.ActionResult) []string { 771 ret := make([]string, len(items)) 772 for i, a := range items { 773 ret[i] = stringify(a) 774 } 775 return ret 776 } 777 778 func stringify(r params.ActionResult) string { 779 a := r.Action 780 if a == nil { 781 a = ¶ms.Action{} 782 } 783 return fmt.Sprintf("%s-%s-%#v-%s-%s-%#v", a.Tag, a.Name, a.Parameters, r.Status, r.Message, r.Output) 784 }