|
15 | 15 | import json |
16 | 16 | import pytest |
17 | 17 | import subprocess |
| 18 | +import sys |
18 | 19 |
|
19 | 20 | from awscli.customizations import sessionmanager |
20 | 21 | from awscli.testutils import mock, unittest |
@@ -427,3 +428,159 @@ def test_sanitize_plugin_version(self, version, expected_result): |
427 | 428 | def test_is_valid_version(self, version, expected_result): |
428 | 429 | assert expected_result == \ |
429 | 430 | self.version_requirement._is_valid_version(version) |
| 431 | + |
| 432 | + |
| 433 | +class TestRecommendedMinimumVersionRequirement: |
| 434 | + version_requirement = sessionmanager.VersionRequirement( |
| 435 | + min_version="1.2.764.0" |
| 436 | + ) |
| 437 | + |
| 438 | + @pytest.mark.parametrize( |
| 439 | + "version, expected_result", |
| 440 | + [ |
| 441 | + # The first version with the capability must satisfy the check. |
| 442 | + ("1.2.764.0", True), |
| 443 | + ("1.2.764", True), |
| 444 | + ("1.2.764.1", True), |
| 445 | + ("1.2.765.0", True), |
| 446 | + ("1.3", True), |
| 447 | + ("2.0.0.0", True), |
| 448 | + ("\r\n1.2. 764.0", True), |
| 449 | + # Anything below the threshold does not. |
| 450 | + ("1.2.763.9", False), |
| 451 | + ("1.2.763", False), |
| 452 | + ("1.2.497.0", False), |
| 453 | + ("1.2", False), |
| 454 | + ("1", False), |
| 455 | + ("0.9.999.9", False), |
| 456 | + # Unparseable versions never satisfy the check. |
| 457 | + ("invalid_version", False), |
| 458 | + ("", False), |
| 459 | + ], |
| 460 | + ) |
| 461 | + def test_meets_requirement_inclusive(self, version, expected_result): |
| 462 | + assert expected_result == \ |
| 463 | + self.version_requirement.meets_requirement( |
| 464 | + version, inclusive=True |
| 465 | + ) |
| 466 | + |
| 467 | + @pytest.mark.parametrize( |
| 468 | + "version, expected_result", |
| 469 | + [ |
| 470 | + ("1.2.764.0", True), |
| 471 | + ("\r\n1.2.764.0\n", True), |
| 472 | + ("1.2.764", True), |
| 473 | + ("invalid_version", False), |
| 474 | + ("", False), |
| 475 | + ("1.1.1.1.1", False), |
| 476 | + ], |
| 477 | + ) |
| 478 | + def test_is_valid_plugin_version(self, version, expected_result): |
| 479 | + assert expected_result == \ |
| 480 | + self.version_requirement.is_valid_plugin_version(version) |
| 481 | + |
| 482 | + |
| 483 | +class TestOutdatedPluginVersionWarning(unittest.TestCase): |
| 484 | + |
| 485 | + def setUp(self): |
| 486 | + self.session = mock.Mock(botocore.session.Session) |
| 487 | + self.client = mock.Mock() |
| 488 | + self.region = 'us-west-2' |
| 489 | + self.endpoint_url = 'testUrl' |
| 490 | + self.client.meta.region_name = self.region |
| 491 | + self.client.meta.endpoint_url = self.endpoint_url |
| 492 | + self.session.create_client.return_value = self.client |
| 493 | + self.caller = sessionmanager.StartSessionCaller(self.session) |
| 494 | + |
| 495 | + self.parsed_globals = mock.Mock() |
| 496 | + self.parsed_globals.profile = 'user_profile' |
| 497 | + |
| 498 | + self.start_session_params = {"Target": "i-123456789"} |
| 499 | + self.client.start_session.return_value = { |
| 500 | + "SessionId": "session-id", |
| 501 | + "TokenValue": "token-value", |
| 502 | + "StreamUrl": "stream-url", |
| 503 | + } |
| 504 | + |
| 505 | + def _invoke_with_plugin_version(self, plugin_version): |
| 506 | + with mock.patch( |
| 507 | + 'awscli.customizations.sessionmanager.check_output' |
| 508 | + ) as mock_check_output, mock.patch( |
| 509 | + 'awscli.customizations.sessionmanager.check_call' |
| 510 | + ) as mock_check_call, mock.patch( |
| 511 | + 'awscli.customizations.sessionmanager.uni_print' |
| 512 | + ) as mock_uni_print: |
| 513 | + mock_check_output.return_value = plugin_version |
| 514 | + mock_check_call.return_value = 0 |
| 515 | + rc = self.caller.invoke( |
| 516 | + 'ssm', 'StartSession', self.start_session_params, |
| 517 | + self.parsed_globals |
| 518 | + ) |
| 519 | + return rc, mock_uni_print |
| 520 | + |
| 521 | + def _warning_messages(self, mock_uni_print): |
| 522 | + return [ |
| 523 | + call_args[0][0] for call_args in mock_uni_print.call_args_list |
| 524 | + if call_args[0] |
| 525 | + and call_args[0][0] == |
| 526 | + sessionmanager.OUTDATED_PLUGIN_VERSION_MESSAGE |
| 527 | + ] |
| 528 | + |
| 529 | + def test_warns_when_plugin_version_is_below_threshold(self): |
| 530 | + rc, mock_uni_print = self._invoke_with_plugin_version("1.2.763.0\n") |
| 531 | + # The warning is advisory only and must not fail the request. |
| 532 | + self.assertEqual(rc, 0) |
| 533 | + self.assertEqual(len(self._warning_messages(mock_uni_print)), 1) |
| 534 | + mock_uni_print.assert_called_with( |
| 535 | + sessionmanager.OUTDATED_PLUGIN_VERSION_MESSAGE, sys.stderr |
| 536 | + ) |
| 537 | + |
| 538 | + def test_warns_when_plugin_version_predates_env_var_support(self): |
| 539 | + rc, mock_uni_print = self._invoke_with_plugin_version("1.2.0.0\n") |
| 540 | + self.assertEqual(rc, 0) |
| 541 | + self.assertEqual(len(self._warning_messages(mock_uni_print)), 1) |
| 542 | + |
| 543 | + def test_no_warning_at_exact_threshold_version(self): |
| 544 | + rc, mock_uni_print = self._invoke_with_plugin_version("1.2.764.0\n") |
| 545 | + self.assertEqual(rc, 0) |
| 546 | + self.assertEqual(self._warning_messages(mock_uni_print), []) |
| 547 | + |
| 548 | + def test_no_warning_when_plugin_version_is_newer(self): |
| 549 | + rc, mock_uni_print = self._invoke_with_plugin_version("1.2.765.0\n") |
| 550 | + self.assertEqual(rc, 0) |
| 551 | + self.assertEqual(self._warning_messages(mock_uni_print), []) |
| 552 | + |
| 553 | + def test_no_warning_when_plugin_version_is_unparseable(self): |
| 554 | + rc, mock_uni_print = self._invoke_with_plugin_version( |
| 555 | + "not_a_version\n" |
| 556 | + ) |
| 557 | + self.assertEqual(rc, 0) |
| 558 | + self.assertEqual(self._warning_messages(mock_uni_print), []) |
| 559 | + |
| 560 | + def test_outdated_plugin_still_receives_start_session_response(self): |
| 561 | + # An outdated plugin must keep the existing fallback behavior of |
| 562 | + # receiving the response directly rather than via an env var. |
| 563 | + with mock.patch( |
| 564 | + 'awscli.customizations.sessionmanager.check_output' |
| 565 | + ) as mock_check_output, mock.patch( |
| 566 | + 'awscli.customizations.sessionmanager.check_call' |
| 567 | + ) as mock_check_call, mock.patch( |
| 568 | + 'awscli.customizations.sessionmanager.uni_print' |
| 569 | + ): |
| 570 | + mock_check_output.return_value = "1.2.0.0\n" |
| 571 | + mock_check_call.return_value = 0 |
| 572 | + rc = self.caller.invoke( |
| 573 | + 'ssm', 'StartSession', self.start_session_params, |
| 574 | + self.parsed_globals |
| 575 | + ) |
| 576 | + |
| 577 | + self.assertEqual(rc, 0) |
| 578 | + check_call_args = mock_check_call.call_args[0][0] |
| 579 | + self.assertEqual( |
| 580 | + json.loads(check_call_args[1]), |
| 581 | + { |
| 582 | + "SessionId": "session-id", |
| 583 | + "TokenValue": "token-value", |
| 584 | + "StreamUrl": "stream-url", |
| 585 | + }, |
| 586 | + ) |
0 commit comments