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