github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/acceptancetests/jujupy/tests/test_status.py (about) 1 from collections import defaultdict 2 from datetime import ( 3 datetime, 4 timedelta, 5 ) 6 from dateutil import tz 7 try: 8 from mock import patch 9 except ImportError: 10 from unittest.mock import patch 11 import types 12 13 from jujupy.exceptions import ( 14 AgentError, 15 AgentUnresolvedError, 16 AppError, 17 ErroredUnit, 18 HookFailedError, 19 InstallError, 20 MachineError, 21 ProvisioningError, 22 StatusError, 23 StuckAllocatingError, 24 UnitError, 25 ) 26 from jujupy.tests.test_client import ( 27 TestModelClient, 28 ) 29 from jujupy.status import ( 30 Status, 31 StatusItem 32 ) 33 from tests import ( 34 TestCase, 35 FakeHomeTestCase, 36 ) 37 38 39 class TestStatusItem(TestCase): 40 41 @staticmethod 42 def make_status_item(status_name, item_name, **kwargs): 43 return StatusItem(status_name, item_name, {status_name: kwargs}) 44 45 def assertIsType(self, obj, target_type): 46 self.assertIs(type(obj), target_type) 47 48 def test_datetime_since(self): 49 item = self.make_status_item(StatusItem.JUJU, '0', 50 since='19 Aug 2016 05:36:42Z') 51 target = datetime(2016, 8, 19, 5, 36, 42, tzinfo=tz.gettz('UTC')) 52 self.assertEqual(item.datetime_since, target) 53 54 def test_datetime_since_lxd(self): 55 UTC = tz.gettz('UTC') 56 item = self.make_status_item(StatusItem.JUJU, '0', 57 since='30 Nov 2016 09:58:43-05:00') 58 target = datetime(2016, 11, 30, 14, 58, 43, tzinfo=UTC) 59 self.assertEqual(item.datetime_since.astimezone(UTC), target) 60 61 def test_datetime_since_none(self): 62 item = self.make_status_item(StatusItem.JUJU, '0') 63 self.assertIsNone(item.datetime_since) 64 65 def test_to_exception_good(self): 66 item = self.make_status_item(StatusItem.JUJU, '0', current='idle') 67 self.assertIsNone(item.to_exception()) 68 69 def test_to_exception_machine_error(self): 70 item = self.make_status_item(StatusItem.MACHINE, '0', current='error') 71 self.assertIsType(item.to_exception(), MachineError) 72 73 def test_to_exception_provisioning_error(self): 74 item = self.make_status_item(StatusItem.MACHINE, '0', 75 current='provisioning error') 76 self.assertIsType(item.to_exception(), ProvisioningError) 77 78 def test_to_exception_stuck_allocating(self): 79 item = self.make_status_item(StatusItem.MACHINE, '0', 80 current='allocating', message='foo') 81 with self.assertRaisesRegexp( 82 StuckAllocatingError, 83 "\('0', 'Stuck allocating. Last message: foo'\)"): 84 raise item.to_exception() 85 86 def test_to_exception_allocating_unit(self): 87 item = self.make_status_item(StatusItem.JUJU, '0', 88 current='allocating', message='foo') 89 self.assertIs(None, item.to_exception()) 90 91 def test_to_exception_app_error(self): 92 item = self.make_status_item(StatusItem.APPLICATION, '0', 93 current='error') 94 self.assertIsType(item.to_exception(), AppError) 95 96 def test_to_exception_unit_error(self): 97 item = self.make_status_item(StatusItem.WORKLOAD, 'fake/0', 98 current='error', 99 message='generic unit error') 100 self.assertIsType(item.to_exception(), UnitError) 101 102 def test_to_exception_hook_failed_error(self): 103 item = self.make_status_item(StatusItem.WORKLOAD, 'fake/0', 104 current='error', 105 message='hook failed: "bad hook"') 106 self.assertIsType(item.to_exception(), HookFailedError) 107 108 def test_to_exception_install_error(self): 109 item = self.make_status_item(StatusItem.WORKLOAD, 'fake/0', 110 current='error', 111 message='hook failed: "install error"') 112 self.assertIsType(item.to_exception(), InstallError) 113 114 def make_agent_item_ago(self, minutes): 115 now = datetime.utcnow() 116 then = now - timedelta(minutes=minutes) 117 then_str = then.strftime('%d %b %Y %H:%M:%SZ') 118 return self.make_status_item(StatusItem.JUJU, '0', current='error', 119 message='some error', since=then_str) 120 121 def test_to_exception_agent_error(self): 122 item = self.make_agent_item_ago(minutes=3) 123 self.assertIsType(item.to_exception(), AgentError) 124 125 def test_to_exception_agent_error_no_since(self): 126 item = self.make_status_item(StatusItem.JUJU, '0', current='error') 127 self.assertIsType(item.to_exception(), AgentError) 128 129 def test_to_exception_agent_unresolved_error(self): 130 item = self.make_agent_item_ago(minutes=6) 131 self.assertIsType(item.to_exception(), AgentUnresolvedError) 132 133 134 class TestStatus(FakeHomeTestCase): 135 136 def test_model_name(self): 137 status = Status({'model': {'name': 'bar'}}, '') 138 self.assertEqual('bar', status.model_name) 139 140 def test_iter_machines_no_containers(self): 141 status = Status({ 142 'machines': { 143 '1': {'foo': 'bar', 'containers': {'1/lxc/0': {'baz': 'qux'}}} 144 }, 145 'applications': {}}, '') 146 self.assertEqual(list(status.iter_machines()), 147 [('1', status.status['machines']['1'])]) 148 149 def test_iter_machines_containers(self): 150 status = Status({ 151 'machines': { 152 '1': {'foo': 'bar', 'containers': {'1/lxc/0': {'baz': 'qux'}}} 153 }, 154 'applications': {}}, '') 155 self.assertEqual(list(status.iter_machines(containers=True)), [ 156 ('1', status.status['machines']['1']), 157 ('1/lxc/0', {'baz': 'qux'}), 158 ]) 159 160 def test__iter_units_in_application(self): 161 status = Status({}, '') 162 app_status = { 163 'units': {'jenkins/1': {'subordinates': {'sub': {'baz': 'qux'}}}} 164 } 165 expected = [ 166 ('jenkins/1', {'subordinates': {'sub': {'baz': 'qux'}}}), 167 ('sub', {'baz': 'qux'})] 168 self.assertItemsEqual(expected, 169 status._iter_units_in_application(app_status)) 170 171 def test_agent_items_empty(self): 172 status = Status({'machines': {}, 'applications': {}}, '') 173 self.assertItemsEqual([], status.agent_items()) 174 175 def test_agent_items(self): 176 status = Status({ 177 'machines': { 178 '1': {'foo': 'bar'} 179 }, 180 'applications': { 181 'jenkins': { 182 'units': { 183 'jenkins/1': { 184 'subordinates': { 185 'sub': {'baz': 'qux'} 186 } 187 } 188 } 189 } 190 } 191 }, '') 192 expected = [ 193 ('1', {'foo': 'bar'}), 194 ('jenkins/1', {'subordinates': {'sub': {'baz': 'qux'}}}), 195 ('sub', {'baz': 'qux'})] 196 self.assertItemsEqual(expected, status.agent_items()) 197 198 def test_agent_items_containers(self): 199 status = Status({ 200 'machines': { 201 '1': {'foo': 'bar', 'containers': { 202 '2': {'qux': 'baz'}, 203 }} 204 }, 205 'applications': {} 206 }, '') 207 expected = [ 208 ('1', {'foo': 'bar', 'containers': {'2': {'qux': 'baz'}}}), 209 ('2', {'qux': 'baz'}) 210 ] 211 self.assertItemsEqual(expected, status.agent_items()) 212 213 def get_unit_agent_states_data(self): 214 status = Status({ 215 'applications': { 216 'jenkins': { 217 'units': {'jenkins/0': {'agent-state': 'good'}, 218 'jenkins/1': {'agent-state': 'bad'}}, 219 }, 220 'fakejob': { 221 'life': 'dying', 222 'units': {'fakejob/0': {'agent-state': 'good'}}, 223 }, 224 } 225 }, '') 226 expected = { 227 'good': ['jenkins/0'], 228 'bad': ['jenkins/1'], 229 'dying': ['fakejob/0'], 230 } 231 return status, expected 232 233 def test_unit_agent_states_new(self): 234 (status, expected) = self.get_unit_agent_states_data() 235 actual = status.unit_agent_states() 236 self.assertEqual(expected, actual) 237 238 def test_unit_agent_states_existing(self): 239 (status, expected) = self.get_unit_agent_states_data() 240 actual = defaultdict(list) 241 status.unit_agent_states(actual) 242 self.assertEqual(expected, actual) 243 244 def test_get_service_count_zero(self): 245 status = Status({ 246 'machines': { 247 '1': {'agent-state': 'good'}, 248 '2': {}, 249 }, 250 }, '') 251 self.assertEqual(0, status.get_service_count()) 252 253 def test_get_service_count(self): 254 status = Status({ 255 'machines': { 256 '1': {'agent-state': 'good'}, 257 '2': {}, 258 }, 259 'applications': { 260 'jenkins': { 261 'units': { 262 'jenkins/1': {'agent-state': 'bad'}, 263 } 264 }, 265 'dummy-sink': { 266 'units': { 267 'dummy-sink/0': {'agent-state': 'started'}, 268 } 269 }, 270 'juju-reports': { 271 'units': { 272 'juju-reports/0': {'agent-state': 'pending'}, 273 } 274 } 275 } 276 }, '') 277 self.assertEqual(3, status.get_service_count()) 278 279 def test_get_service_unit_count_zero(self): 280 status = Status({ 281 'machines': { 282 '1': {'agent-state': 'good'}, 283 '2': {}, 284 }, 285 }, '') 286 self.assertEqual(0, status.get_service_unit_count('jenkins')) 287 288 def test_get_service_unit_count(self): 289 status = Status({ 290 'machines': { 291 '1': {'agent-state': 'good'}, 292 '2': {}, 293 }, 294 'applications': { 295 'jenkins': { 296 'units': { 297 'jenkins/1': {'agent-state': 'bad'}, 298 'jenkins/2': {'agent-state': 'bad'}, 299 'jenkins/3': {'agent-state': 'bad'}, 300 } 301 } 302 } 303 }, '') 304 self.assertEqual(3, status.get_service_unit_count('jenkins')) 305 306 def test_get_unit(self): 307 status = Status({ 308 'machines': { 309 '1': {}, 310 }, 311 'applications': { 312 'jenkins': { 313 'units': { 314 'jenkins/1': {'agent-state': 'bad'}, 315 } 316 }, 317 'dummy-sink': { 318 'units': { 319 'jenkins/2': {'agent-state': 'started'}, 320 } 321 }, 322 } 323 }, '') 324 self.assertEqual( 325 status.get_unit('jenkins/1'), {'agent-state': 'bad'}) 326 self.assertEqual( 327 status.get_unit('jenkins/2'), {'agent-state': 'started'}) 328 with self.assertRaisesRegexp(KeyError, 'jenkins/3'): 329 status.get_unit('jenkins/3') 330 331 def test_service_subordinate_units(self): 332 status = Status({ 333 'machines': { 334 '1': {}, 335 }, 336 'applications': { 337 'ubuntu': {}, 338 'jenkins': { 339 'units': { 340 'jenkins/1': { 341 'subordinates': { 342 'chaos-monkey/0': {'agent-state': 'started'}, 343 } 344 } 345 } 346 }, 347 'dummy-sink': { 348 'units': { 349 'jenkins/2': { 350 'subordinates': { 351 'chaos-monkey/1': {'agent-state': 'started'} 352 } 353 }, 354 'jenkins/3': { 355 'subordinates': { 356 'chaos-monkey/2': {'agent-state': 'started'} 357 } 358 } 359 } 360 } 361 } 362 }, '') 363 self.assertItemsEqual( 364 status.service_subordinate_units('ubuntu'), 365 []) 366 self.assertItemsEqual( 367 status.service_subordinate_units('jenkins'), 368 [('chaos-monkey/0', {'agent-state': 'started'},)]) 369 self.assertItemsEqual( 370 status.service_subordinate_units('dummy-sink'), [ 371 ('chaos-monkey/1', {'agent-state': 'started'}), 372 ('chaos-monkey/2', {'agent-state': 'started'})] 373 ) 374 375 def test_get_open_ports(self): 376 status = Status({ 377 'machines': { 378 '1': {}, 379 }, 380 'applications': { 381 'jenkins': { 382 'units': { 383 'jenkins/1': {'agent-state': 'bad'}, 384 } 385 }, 386 'dummy-sink': { 387 'units': { 388 'jenkins/2': {'open-ports': ['42/tcp']}, 389 } 390 }, 391 } 392 }, '') 393 self.assertEqual(status.get_open_ports('jenkins/1'), []) 394 self.assertEqual(status.get_open_ports('jenkins/2'), ['42/tcp']) 395 396 def test_agent_states_with_agent_state(self): 397 status = Status({ 398 'machines': { 399 '1': {'agent-state': 'good'}, 400 '2': {}, 401 }, 402 'applications': { 403 'jenkins': { 404 'units': { 405 'jenkins/1': {'agent-state': 'bad'}, 406 'jenkins/2': {'agent-state': 'good'}, 407 } 408 } 409 } 410 }, '') 411 expected = { 412 'good': ['1', 'jenkins/2'], 413 'bad': ['jenkins/1'], 414 'no-agent': ['2'], 415 } 416 self.assertEqual(expected, status.agent_states()) 417 418 def test_agent_states_with_agent_status(self): 419 status = Status({ 420 'machines': { 421 '1': {'agent-state': 'good'}, 422 '2': {}, 423 }, 424 'applications': { 425 'jenkins': { 426 'units': { 427 'jenkins/1': {'agent-status': {'current': 'bad'}}, 428 'jenkins/2': {'agent-status': {'current': 'good'}}, 429 'jenkins/3': {}, 430 } 431 } 432 } 433 }, '') 434 expected = { 435 'good': ['1', 'jenkins/2'], 436 'bad': ['jenkins/1'], 437 'no-agent': ['2', 'jenkins/3'], 438 } 439 self.assertEqual(expected, status.agent_states()) 440 441 def test_agent_states_with_juju_status(self): 442 status = Status({ 443 'machines': { 444 '1': {'juju-status': {'current': 'good'}}, 445 '2': {}, 446 }, 447 'applications': { 448 'jenkins': { 449 'units': { 450 'jenkins/1': {'juju-status': {'current': 'bad'}}, 451 'jenkins/2': {'juju-status': {'current': 'good'}}, 452 'jenkins/3': {}, 453 } 454 } 455 } 456 }, '') 457 expected = { 458 'good': ['1', 'jenkins/2'], 459 'bad': ['jenkins/1'], 460 'no-agent': ['2', 'jenkins/3'], 461 } 462 self.assertEqual(expected, status.agent_states()) 463 464 def test_agent_states_with_dying(self): 465 status = Status({ 466 'machines': {}, 467 'applications': { 468 'jenkins': { 469 'life': 'alive', 470 'units': { 471 'jenkins/1': {'juju-status': {'current': 'bad'}}, 472 'jenkins/2': {'juju-status': {'current': 'good'}}, 473 } 474 }, 475 'fakejob': { 476 'life': 'dying', 477 'units': { 478 'fakejob/1': {'juju-status': {'current': 'bad'}}, 479 'fakejob/2': {'juju-status': {'current': 'good'}}, 480 } 481 }, 482 } 483 }, '') 484 expected = { 485 'good': ['jenkins/2'], 486 'bad': ['jenkins/1'], 487 'dying': ['fakejob/1', 'fakejob/2'], 488 } 489 self.assertEqual(expected, status.agent_states()) 490 491 def test_check_agents_started_not_started(self): 492 status = Status({ 493 'machines': { 494 '1': {'agent-state': 'good'}, 495 '2': {}, 496 }, 497 'applications': { 498 'jenkins': { 499 'units': { 500 'jenkins/1': {'agent-state': 'bad'}, 501 'jenkins/2': {'agent-state': 'good'}, 502 } 503 } 504 } 505 }, '') 506 self.assertEqual(status.agent_states(), 507 status.check_agents_started('env1')) 508 509 def test_check_agents_started_all_started_with_agent_state(self): 510 status = Status({ 511 'machines': { 512 '1': {'agent-state': 'started'}, 513 '2': {'agent-state': 'started'}, 514 }, 515 'applications': { 516 'jenkins': { 517 'units': { 518 'jenkins/1': { 519 'agent-state': 'started', 520 'subordinates': { 521 'sub1': { 522 'agent-state': 'started' 523 } 524 } 525 }, 526 'jenkins/2': {'agent-state': 'started'}, 527 } 528 } 529 } 530 }, '') 531 self.assertIsNone(status.check_agents_started('env1')) 532 533 def test_check_agents_started_all_started_with_agent_status(self): 534 status = Status({ 535 'machines': { 536 '1': {'agent-state': 'started'}, 537 '2': {'agent-state': 'started'}, 538 }, 539 'applications': { 540 'jenkins': { 541 'units': { 542 'jenkins/1': { 543 'agent-status': {'current': 'idle'}, 544 'subordinates': { 545 'sub1': { 546 'agent-status': {'current': 'idle'} 547 } 548 } 549 }, 550 'jenkins/2': {'agent-status': {'current': 'idle'}}, 551 } 552 } 553 } 554 }, '') 555 self.assertIsNone(status.check_agents_started('env1')) 556 557 def test_check_agents_started_dying(self): 558 status = Status({ 559 'machines': { 560 '1': {'agent-state': 'started'}, 561 '2': {'agent-state': 'started'}, 562 }, 563 'applications': { 564 'jenkins': { 565 'units': { 566 'jenkins/1': { 567 'agent-status': {'current': 'idle'}, 568 'subordinates': { 569 'sub1': { 570 'agent-status': {'current': 'idle'} 571 } 572 } 573 }, 574 'jenkins/2': {'agent-status': {'current': 'idle'}}, 575 }, 576 'life': 'dying', 577 } 578 } 579 }, '') 580 self.assertEqual(status.agent_states(), 581 status.check_agents_started('env1')) 582 583 def test_check_agents_started_agent_error(self): 584 status = Status({ 585 'machines': { 586 '1': {'agent-state': 'any-error'}, 587 }, 588 'applications': {} 589 }, '') 590 with self.assertRaisesRegexp(ErroredUnit, 591 '1 is in state any-error'): 592 status.check_agents_started('env1') 593 594 def do_check_agents_started_agent_state_info_failure(self, failure): 595 status = Status({ 596 'machines': {'0': { 597 'agent-state-info': failure}}, 598 'applications': {}, 599 }, '') 600 with self.assertRaises(ErroredUnit) as e_cxt: 601 status.check_agents_started() 602 e = e_cxt.exception 603 self.assertEqual( 604 str(e), '0 is in state {}'.format(failure)) 605 self.assertEqual(e.unit_name, '0') 606 self.assertEqual(e.state, failure) 607 608 def do_check_agents_started_juju_status_failure(self, failure): 609 status = Status({ 610 'machines': { 611 '0': { 612 'juju-status': { 613 'current': 'error', 614 'message': failure} 615 }, 616 } 617 }, '') 618 with self.assertRaises(ErroredUnit) as e_cxt: 619 status.check_agents_started() 620 e = e_cxt.exception 621 # if message is blank, the failure should reflect the state instead 622 if not failure: 623 failure = 'error' 624 self.assertEqual( 625 str(e), '0 is in state {}'.format(failure)) 626 self.assertEqual(e.unit_name, '0') 627 self.assertEqual(e.state, failure) 628 629 def test_check_agents_started_read_juju_status_error(self): 630 failures = ['no "centos7" images in us-east-1 with arches [amd64]', 631 'sending new instance request: GCE operation ' + 632 '"operation-143" failed', ''] 633 for failure in failures: 634 self.do_check_agents_started_juju_status_failure(failure) 635 636 def test_check_agents_started_read_agent_state_info_error(self): 637 failures = ['cannot set up groups foobar', 'cannot run instance', 638 'cannot run instances', 'error executing "lxc-start"'] 639 for failure in failures: 640 self.do_check_agents_started_agent_state_info_failure(failure) 641 642 def test_check_agents_started_agent_info_error(self): 643 # Sometimes the error is indicated in a special 'agent-state-info' 644 # field. 645 status = Status({ 646 'machines': { 647 '1': {'agent-state-info': 'any-error'}, 648 }, 649 'applications': {} 650 }, '') 651 with self.assertRaisesRegexp(ErroredUnit, 652 '1 is in state any-error'): 653 status.check_agents_started('env1') 654 655 def test_get_agent_versions_1x(self): 656 status = Status({ 657 'machines': { 658 '1': {'agent-version': '1.6.2'}, 659 '2': {'agent-version': '1.6.1'}, 660 }, 661 'applications': { 662 'jenkins': { 663 'units': { 664 'jenkins/0': { 665 'agent-version': '1.6.1'}, 666 'jenkins/1': {}, 667 }, 668 } 669 } 670 }, '') 671 self.assertEqual({ 672 '1.6.2': {'1'}, 673 '1.6.1': {'jenkins/0', '2'}, 674 'unknown': {'jenkins/1'}, 675 }, status.get_agent_versions()) 676 677 def test_get_agent_versions_2x(self): 678 status = Status({ 679 'machines': { 680 '1': {'juju-status': {'version': '1.6.2'}}, 681 '2': {'juju-status': {'version': '1.6.1'}}, 682 }, 683 'applications': { 684 'jenkins': { 685 'units': { 686 'jenkins/0': { 687 'juju-status': {'version': '1.6.1'}}, 688 'jenkins/1': {}, 689 }, 690 } 691 } 692 }, '') 693 self.assertEqual({ 694 '1.6.2': {'1'}, 695 '1.6.1': {'jenkins/0', '2'}, 696 'unknown': {'jenkins/1'}, 697 }, status.get_agent_versions()) 698 699 def test_iter_new_machines(self): 700 old_status = Status({ 701 'machines': { 702 'bar': 'bar_info', 703 } 704 }, '') 705 new_status = Status({ 706 'machines': { 707 'foo': 'foo_info', 708 'bar': 'bar_info', 709 } 710 }, '') 711 self.assertItemsEqual(new_status.iter_new_machines(old_status), 712 [('foo', 'foo_info')]) 713 714 def test_iter_new_machines_no_containers(self): 715 bar_info = {'containers': {'bar/lxd/1': {}}} 716 old_status = Status({ 717 'machines': { 718 'bar': bar_info, 719 } 720 }, '') 721 foo_info = {'containers': {'foo/lxd/1': {}}} 722 new_status = Status({ 723 'machines': { 724 'foo': foo_info, 725 'bar': bar_info, 726 } 727 }, '') 728 self.assertItemsEqual(new_status.iter_new_machines(old_status, 729 containers=False), 730 [('foo', foo_info)]) 731 732 def test_iter_new_machines_with_containers(self): 733 bar_info = {'containers': {'bar/lxd/1': {}}} 734 old_status = Status({ 735 'machines': { 736 'bar': bar_info, 737 } 738 }, '') 739 foo_info = {'containers': {'foo/lxd/1': {}}} 740 new_status = Status({ 741 'machines': { 742 'foo': foo_info, 743 'bar': bar_info, 744 } 745 }, '') 746 self.assertItemsEqual(new_status.iter_new_machines(old_status, 747 containers=True), 748 [('foo', foo_info), ('foo/lxd/1', {})]) 749 750 def test_get_instance_id(self): 751 status = Status({ 752 'machines': { 753 '0': {'instance-id': 'foo-bar'}, 754 '1': {}, 755 } 756 }, '') 757 self.assertEqual(status.get_instance_id('0'), 'foo-bar') 758 with self.assertRaises(KeyError): 759 status.get_instance_id('1') 760 with self.assertRaises(KeyError): 761 status.get_instance_id('2') 762 763 def test_get_machine_dns_name(self): 764 status = Status({ 765 'machines': { 766 '0': {'dns-name': '255.1.1.0'}, 767 '1': {}, 768 } 769 }, '') 770 self.assertEqual(status.get_machine_dns_name('0'), '255.1.1.0') 771 with self.assertRaisesRegexp(KeyError, 'dns-name'): 772 status.get_machine_dns_name('1') 773 with self.assertRaisesRegexp(KeyError, '2'): 774 status.get_machine_dns_name('2') 775 776 def test_from_text(self): 777 text = TestModelClient.make_status_yaml( 778 'agent-state', 'pending', 'horsefeathers').decode('ascii') 779 status = Status.from_text(text) 780 self.assertEqual(status.status_text, text) 781 self.assertEqual(status.status, { 782 'model': {'name': 'foo'}, 783 'machines': {'0': {'agent-state': 'pending'}}, 784 'applications': {'jenkins': {'units': {'jenkins/0': { 785 'agent-state': 'horsefeathers'}}}} 786 }) 787 788 def test_iter_units(self): 789 started_unit = {'agent-state': 'started'} 790 unit_with_subordinates = { 791 'agent-state': 'started', 792 'subordinates': { 793 'ntp/0': started_unit, 794 'nrpe/0': started_unit, 795 }, 796 } 797 status = Status({ 798 'machines': { 799 '1': {'agent-state': 'started'}, 800 }, 801 'applications': { 802 'jenkins': { 803 'units': { 804 'jenkins/0': unit_with_subordinates, 805 } 806 }, 807 'application': { 808 'units': { 809 'application/0': started_unit, 810 'application/1': started_unit, 811 } 812 }, 813 } 814 }, '') 815 expected = [ 816 ('application/0', started_unit), 817 ('application/1', started_unit), 818 ('jenkins/0', unit_with_subordinates), 819 ('nrpe/0', started_unit), 820 ('ntp/0', started_unit), 821 ] 822 gen = status.iter_units() 823 self.assertIsInstance(gen, types.GeneratorType) 824 self.assertEqual(expected, list(gen)) 825 826 @staticmethod 827 def run_iter_status(): 828 status = Status({ 829 'machines': { 830 '0': { 831 'juju-status': { 832 'current': 'idle', 833 'since': 'DD MM YYYY hh:mm:ss', 834 'version': '2.0.0', 835 }, 836 'machine-status': { 837 'current': 'running', 838 'message': 'Running', 839 'since': 'DD MM YYYY hh:mm:ss', 840 }, 841 }, 842 '1': { 843 'juju-status': { 844 'current': 'idle', 845 'since': 'DD MM YYYY hh:mm:ss', 846 'version': '2.0.0', 847 }, 848 'machine-status': { 849 'current': 'running', 850 'message': 'Running', 851 'since': 'DD MM YYYY hh:mm:ss', 852 }, 853 }, 854 }, 855 'applications': { 856 'fakejob': { 857 'application-status': { 858 'current': 'idle', 859 'since': 'DD MM YYYY hh:mm:ss', 860 }, 861 'units': { 862 'fakejob/0': { 863 'workload-status': { 864 'current': 'maintenance', 865 'message': 'Started', 866 'since': 'DD MM YYYY hh:mm:ss', 867 }, 868 'juju-status': { 869 'current': 'idle', 870 'since': 'DD MM YYYY hh:mm:ss', 871 'version': '2.0.0', 872 }, 873 }, 874 'fakejob/1': { 875 'workload-status': { 876 'current': 'maintenance', 877 'message': 'Started', 878 'since': 'DD MM YYYY hh:mm:ss', 879 }, 880 'juju-status': { 881 'current': 'idle', 882 'since': 'DD MM YYYY hh:mm:ss', 883 'version': '2.0.0', 884 }, 885 }, 886 }, 887 } 888 }, 889 }, '') 890 for sub_status in status.iter_status(): 891 yield sub_status 892 893 def test_iter_status_range(self): 894 status_set = set([(status_item.item_name, status_item.status_name) 895 for status_item in self.run_iter_status()]) 896 self.assertEqual({ 897 ('0', 'juju-status'), ('0', 'machine-status'), 898 ('1', 'juju-status'), ('1', 'machine-status'), 899 ('fakejob', 'application-status'), 900 ('fakejob/0', 'workload-status'), ('fakejob/0', 'juju-status'), 901 ('fakejob/1', 'workload-status'), ('fakejob/1', 'juju-status'), 902 }, status_set) 903 904 def test_iter_status_data(self): 905 min_set = set(['current', 'since']) 906 max_set = set(['current', 'message', 'since', 'version']) 907 for status_item in self.run_iter_status(): 908 if 'fakejob' == status_item.item_name: 909 self.assertEqual(StatusItem.APPLICATION, 910 status_item.status_name) 911 self.assertEqual({'current': 'idle', 912 'since': 'DD MM YYYY hh:mm:ss', 913 }, status_item.status) 914 else: 915 cur_set = set(status_item.status.keys()) 916 self.assertTrue(min_set < cur_set) 917 self.assertTrue(cur_set < max_set) 918 919 def test_iter_status_container(self): 920 status_dict = {'machines': {'0': { 921 'containers': {'0/lxd/0': { 922 'machine-status': 'foo', 923 'juju-status': 'bar', 924 }} 925 }}} 926 status = Status(status_dict, '') 927 machine_0_data = status.status['machines']['0'] 928 container_data = machine_0_data['containers']['0/lxd/0'] 929 self.assertEqual([ 930 StatusItem(StatusItem.MACHINE, '0', machine_0_data), 931 StatusItem(StatusItem.JUJU, '0', machine_0_data), 932 StatusItem(StatusItem.MACHINE, '0/lxd/0', container_data), 933 StatusItem(StatusItem.JUJU, '0/lxd/0', container_data), 934 ], list(status.iter_status())) 935 936 def test_iter_status_subordinate(self): 937 status_dict = { 938 'machines': {}, 939 'applications': { 940 'dummy': { 941 'units': {'dummy/0': { 942 'subordinates': { 943 'dummy-sub/0': {} 944 } 945 }}, 946 } 947 }, 948 } 949 status = Status(status_dict, '') 950 dummy_data = status.status['applications']['dummy'] 951 dummy_0_data = dummy_data['units']['dummy/0'] 952 dummy_sub_0_data = dummy_0_data['subordinates']['dummy-sub/0'] 953 self.assertEqual([ 954 StatusItem(StatusItem.APPLICATION, 'dummy', dummy_data), 955 StatusItem(StatusItem.WORKLOAD, 'dummy/0', dummy_0_data), 956 StatusItem(StatusItem.JUJU, 'dummy/0', dummy_0_data), 957 StatusItem(StatusItem.WORKLOAD, 'dummy-sub/0', dummy_sub_0_data), 958 StatusItem(StatusItem.JUJU, 'dummy-sub/0', dummy_sub_0_data), 959 ], list(status.iter_status())) 960 961 def test_iter_errors(self): 962 status = Status({}, '') 963 retval = [ 964 StatusItem(StatusItem.WORKLOAD, 'job/0', {'current': 'started'}), 965 StatusItem(StatusItem.APPLICATION, 'job', {'current': 'started'}), 966 StatusItem(StatusItem.MACHINE, '0', {'current': 'error'}), 967 ] 968 with patch.object(status, 'iter_status', autospec=True, 969 return_value=retval): 970 errors = list(status.iter_errors()) 971 self.assertEqual(len(errors), 1) 972 self.assertIsInstance(errors[0], MachineError) 973 self.assertEqual(('0', None), errors[0].args) 974 975 def test_iter_errors_ignore_recoverable(self): 976 status = Status({}, '') 977 retval = [ 978 StatusItem(StatusItem.WORKLOAD, 'job/0', {'current': 'error'}), 979 StatusItem(StatusItem.MACHINE, '0', {'current': 'error'}), 980 ] 981 with patch.object(status, 'iter_status', autospec=True, 982 return_value=retval): 983 errors = list(status.iter_errors(ignore_recoverable=True)) 984 self.assertEqual(len(errors), 1) 985 self.assertIsInstance(errors[0], MachineError) 986 self.assertEqual(('0', None), errors[0].args) 987 with patch.object(status, 'iter_status', autospec=True, 988 return_value=retval): 989 recoverable = list(status.iter_errors()) 990 self.assertGreater(len(recoverable), len(errors)) 991 992 def test_check_for_errors_good(self): 993 status = Status({}, '') 994 with patch.object(status, 'iter_errors', autospec=True, 995 return_value=[]) as error_mock: 996 self.assertEqual([], status.check_for_errors()) 997 error_mock.assert_called_once_with(False) 998 999 def test_check_for_errors(self): 1000 status = Status({}, '') 1001 errors = [MachineError('0'), StatusError('2'), UnitError('1')] 1002 with patch.object(status, 'iter_errors', autospec=True, 1003 return_value=errors) as errors_mock: 1004 sorted_errors = status.check_for_errors() 1005 errors_mock.assert_called_once_with(False) 1006 self.assertEqual(sorted_errors[0].args, ('0',)) 1007 self.assertEqual(sorted_errors[1].args, ('1',)) 1008 self.assertEqual(sorted_errors[2].args, ('2',)) 1009 1010 def test_raise_highest_error(self): 1011 status = Status({}, '') 1012 retval = [ 1013 StatusItem(StatusItem.WORKLOAD, 'job/0', {'current': 'error'}), 1014 StatusItem(StatusItem.MACHINE, '0', {'current': 'error'}), 1015 ] 1016 with patch.object(status, 'iter_status', autospec=True, 1017 return_value=retval): 1018 with self.assertRaises(MachineError): 1019 status.raise_highest_error() 1020 1021 def test_raise_highest_error_ignore_recoverable(self): 1022 status = Status({}, '') 1023 retval = [ 1024 StatusItem(StatusItem.WORKLOAD, 'job/0', {'current': 'error'})] 1025 with patch.object(status, 'iter_status', autospec=True, 1026 return_value=retval): 1027 status.raise_highest_error(ignore_recoverable=True) 1028 with self.assertRaises(UnitError): 1029 status.raise_highest_error(ignore_recoverable=False) 1030 1031 def test_get_applications_gets_applications(self): 1032 status = Status({ 1033 'services': {'service': {}}, 1034 'applications': {'application': {}}, 1035 }, '') 1036 self.assertEqual({'application': {}}, status.get_applications())