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