OLD | NEW |
1 # This file is part of the Juju Quickstart Plugin, which lets users set up a | 1 # This file is part of the Juju Quickstart Plugin, which lets users set up a |
2 # Juju environment in very few steps (https://launchpad.net/juju-quickstart). | 2 # Juju environment in very few steps (https://launchpad.net/juju-quickstart). |
3 # Copyright (C) 2013-2014 Canonical Ltd. | 3 # Copyright (C) 2013-2014 Canonical Ltd. |
4 # | 4 # |
5 # This program is free software: you can redistribute it and/or modify it under | 5 # This program is free software: you can redistribute it and/or modify it under |
6 # the terms of the GNU Affero General Public License version 3, as published by | 6 # the terms of the GNU Affero General Public License version 3, as published by |
7 # the Free Software Foundation. | 7 # the Free Software Foundation. |
8 # | 8 # |
9 # This program is distributed in the hope that it will be useful, but WITHOUT | 9 # This program is distributed in the hope that it will be useful, but WITHOUT |
10 # ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, | 10 # ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
(...skipping 707 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
718 'Error': 'tokens can only be created by authenticated users.', | 718 'Error': 'tokens can only be created by authenticated users.', |
719 'ErrorCode': 'unauthorized access' | 719 'ErrorCode': 'unauthorized access' |
720 }) | 720 }) |
721 env.create_auth_token.side_effect = error | 721 env.create_auth_token.side_effect = error |
722 with self.assertRaises(jujuclient.EnvError) as context_manager: | 722 with self.assertRaises(jujuclient.EnvError) as context_manager: |
723 app.create_auth_token(env) | 723 app.create_auth_token(env) |
724 self.assertIs(error, context_manager.exception) | 724 self.assertIs(error, context_manager.exception) |
725 | 725 |
726 | 726 |
727 @helpers.mock_print | 727 @helpers.mock_print |
| 728 class TestCheckEnvironment( |
| 729 ProgramExitTestsMixin, helpers.WatcherDataTestsMixin, |
| 730 unittest.TestCase): |
| 731 |
| 732 def make_env(self, include_data=False, side_effect=None): |
| 733 """Create and return a mock environment object. |
| 734 |
| 735 If include_data is True, set up the object so that a call to status |
| 736 returns a status object containing service and unit data. |
| 737 |
| 738 The side_effect argument can be used to simulate status errors. |
| 739 """ |
| 740 env = mock.Mock() |
| 741 # Set up the get_status return value. |
| 742 status = [] |
| 743 if include_data: |
| 744 status = [self.make_service_change(), self.make_unit_change()] |
| 745 env.get_status.return_value = status |
| 746 env.get_status.side_effect = side_effect |
| 747 return env |
| 748 |
| 749 def patch_get_charm_url(self, return_value=None, side_effect=None): |
| 750 """Patch the get_charm_url helper function.""" |
| 751 mock_get_charm_url = mock.Mock( |
| 752 return_value=return_value, side_effect=side_effect) |
| 753 return mock.patch('quickstart.utils.get_charm_url', mock_get_charm_url) |
| 754 |
| 755 def test_environment_just_bootstrapped(self, mock_print): |
| 756 # The function correctly retrieves the charm URL and machine, and |
| 757 # handles the case when the charm URL is not provided by the user. |
| 758 # In this scenario, the environment has been bootstrapped by |
| 759 # quickstart, so there is no need to check its status. For this reason, |
| 760 # service_data and unit_data should be set to None. |
| 761 env = self.make_env() |
| 762 charm_url = None |
| 763 env_type = 'ec2' |
| 764 bootstrap_node_series = 'trusty' |
| 765 check_preexisting = False |
| 766 with self.patch_get_charm_url( |
| 767 return_value='cs:trusty/juju-gui-42') as mock_get_charm_url: |
| 768 url, machine, service_data, unit_data = app.check_environment( |
| 769 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 770 check_preexisting) |
| 771 # There is no need to call status if the environment was just created. |
| 772 self.assertFalse(env.get_status.called) |
| 773 # The charm URL has been retrieved from charmworld based on the current |
| 774 # bootstrap node series. |
| 775 self.assertEqual('cs:trusty/juju-gui-42', url) |
| 776 mock_get_charm_url.assert_called_once_with(bootstrap_node_series) |
| 777 # Since the bootstrap node series is supported by the GUI charm, the |
| 778 # GUI unit can be deployed to machine 0. |
| 779 self.assertEqual('0', machine) |
| 780 # When not checking for pre-existing service and/or unit, the |
| 781 # corresponding service and unit data are set to None. |
| 782 self.assertIsNone(service_data) |
| 783 self.assertIsNone(unit_data) |
| 784 # Ensure the function output makes sense. |
| 785 self.assertEqual(2, mock_print.call_count) |
| 786 mock_print.assert_has_calls([ |
| 787 mock.call('bootstrap node series: trusty'), |
| 788 mock.call('charm URL: cs:trusty/juju-gui-42'), |
| 789 ]) |
| 790 |
| 791 def test_existing_environment_without_entities(self, mock_print): |
| 792 # The function correctly retrieves the charm URL and machine. |
| 793 # In this scenario, the environment was already bootstrapped, but it |
| 794 # does not include the GUI. For this reason, service_data and unit_data |
| 795 # are set to None. |
| 796 env = self.make_env() |
| 797 charm_url = None |
| 798 env_type = 'ec2' |
| 799 bootstrap_node_series = 'precise' |
| 800 check_preexisting = True |
| 801 with self.patch_get_charm_url( |
| 802 return_value='cs:precise/juju-gui-42') as mock_get_charm_url: |
| 803 url, machine, service_data, unit_data = app.check_environment( |
| 804 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 805 check_preexisting) |
| 806 # The environment status has been retrieved. |
| 807 env.get_status.assert_called_once_with() |
| 808 # The charm URL has been retrieved from charmworld based on the current |
| 809 # bootstrap node series. |
| 810 self.assertEqual('cs:precise/juju-gui-42', url) |
| 811 mock_get_charm_url.assert_called_once_with(bootstrap_node_series) |
| 812 # Since the bootstrap node series is supported by the GUI charm, the |
| 813 # GUI unit can be deployed to machine 0. |
| 814 self.assertEqual('0', machine) |
| 815 # The service and unit data are set to None. |
| 816 self.assertIsNone(service_data) |
| 817 self.assertIsNone(unit_data) |
| 818 # Ensure the function output makes sense. |
| 819 self.assertEqual(2, mock_print.call_count) |
| 820 mock_print.assert_has_calls([ |
| 821 mock.call('bootstrap node series: precise'), |
| 822 mock.call('charm URL: cs:precise/juju-gui-42'), |
| 823 ]) |
| 824 |
| 825 def test_existing_environment_with_entities(self, mock_print): |
| 826 # The function correctly retrieves the charm URL and machine when the |
| 827 # environment is already bootstrapped and includes a Juju GUI unit. |
| 828 # In this case service_data and unit_data are actually populated. |
| 829 env = self.make_env(include_data=True) |
| 830 charm_url = None |
| 831 env_type = 'ec2' |
| 832 bootstrap_node_series = 'precise' |
| 833 check_preexisting = True |
| 834 with self.patch_get_charm_url() as mock_get_charm_url: |
| 835 url, machine, service_data, unit_data = app.check_environment( |
| 836 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 837 check_preexisting) |
| 838 # The environment status has been retrieved. |
| 839 env.get_status.assert_called_once_with() |
| 840 # The charm URL has been retrieved from the environment. |
| 841 self.assertEqual('cs:precise/juju-gui-47', url) |
| 842 self.assertFalse(mock_get_charm_url.called) |
| 843 # Since the bootstrap node series is supported by the GUI charm, the |
| 844 # GUI unit can be safely deployed to machine 0. |
| 845 self.assertEqual('0', machine) |
| 846 # The service and unit data are correctly returned. |
| 847 self.assertEqual(self.make_service_data(), service_data) |
| 848 self.assertEqual(self.make_unit_data(), unit_data) |
| 849 |
| 850 def test_bootstrap_node_series_not_supported(self, mock_print): |
| 851 # If the bootstrap node is not suitable for hosting the Juju GUI unit, |
| 852 # the returned machine is set to None. |
| 853 env = self.make_env() |
| 854 charm_url = None |
| 855 env_type = 'ec2' |
| 856 bootstrap_node_series = 'saucy' |
| 857 check_preexisting = False |
| 858 with self.patch_get_charm_url( |
| 859 return_value='cs:trusty/juju-gui-42') as mock_get_charm_url: |
| 860 url, machine, service_data, unit_data = app.check_environment( |
| 861 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 862 check_preexisting) |
| 863 # The charm URL has been retrieved from charmworld using the most |
| 864 # recent supported series. |
| 865 self.assertEqual('cs:trusty/juju-gui-42', url) |
| 866 mock_get_charm_url.assert_called_once_with('trusty') |
| 867 # The Juju GUI unit cannot be deployed to saucy machine 0. |
| 868 self.assertIsNone(machine) |
| 869 # Ensure the function output makes sense. |
| 870 self.assertEqual(2, mock_print.call_count) |
| 871 mock_print.assert_has_calls([ |
| 872 mock.call('bootstrap node series: saucy'), |
| 873 mock.call('charm URL: cs:trusty/juju-gui-42'), |
| 874 ]) |
| 875 |
| 876 def test_local_provider(self, mock_print): |
| 877 # If the local provider is used the Juju GUI unit cannot be deployed to |
| 878 # machine 0. |
| 879 env = self.make_env() |
| 880 charm_url = None |
| 881 env_type = 'local' |
| 882 bootstrap_node_series = 'trusty' |
| 883 check_preexisting = False |
| 884 with self.patch_get_charm_url(return_value='cs:trusty/juju-gui-42'): |
| 885 url, machine, service_data, unit_data = app.check_environment( |
| 886 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 887 check_preexisting) |
| 888 # The charm URL has been correctly retrieved from charmworld. |
| 889 self.assertEqual('cs:trusty/juju-gui-42', url) |
| 890 # The Juju GUI unit cannot be deployed to localhost. |
| 891 self.assertIsNone(machine) |
| 892 |
| 893 def test_default_charm_url(self, mock_print): |
| 894 # A default charm URL suitable to be deployed in the bootstrap node is |
| 895 # returned if the charmworld API is not reachable. |
| 896 env = self.make_env() |
| 897 charm_url = None |
| 898 env_type = 'ec2' |
| 899 bootstrap_node_series = 'precise' |
| 900 check_preexisting = False |
| 901 with self.patch_get_charm_url(side_effect=IOError('boo!')): |
| 902 url, machine, service_data, unit_data = app.check_environment( |
| 903 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 904 check_preexisting) |
| 905 # The default charm URL for the given series is returned. |
| 906 self.assertEqual(settings.DEFAULT_CHARM_URLS['precise'], url) |
| 907 self.assertEqual('0', machine) |
| 908 |
| 909 def test_most_recent_default_charm_url(self, mock_print): |
| 910 # The default charm URL corresponding to the most recent series |
| 911 # supported by the GUI is returned if the charmworld API is not |
| 912 # reachable and the bootstrap node cannot host the Juju GUI unit. |
| 913 env = self.make_env() |
| 914 charm_url = None |
| 915 env_type = 'ec2' |
| 916 bootstrap_node_series = 'saucy' |
| 917 check_preexisting = False |
| 918 with self.patch_get_charm_url(side_effect=IOError('boo!')): |
| 919 url, machine, service_data, unit_data = app.check_environment( |
| 920 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 921 check_preexisting) |
| 922 # The default charm URL for the given series is returned. |
| 923 series = settings.JUJU_GUI_SUPPORTED_SERIES[-1] |
| 924 self.assertEqual(settings.DEFAULT_CHARM_URLS[series], url) |
| 925 self.assertIsNone(machine) |
| 926 |
| 927 def test_charm_url_provided(self, mock_print): |
| 928 # The function knows when a custom charm URL can be deployed in the |
| 929 # bootstrap node. |
| 930 env = self.make_env() |
| 931 charm_url = 'cs:~juju-gui/trusty/juju-gui-100' |
| 932 env_type = 'ec2' |
| 933 bootstrap_node_series = 'trusty' |
| 934 check_preexisting = False |
| 935 with self.patch_get_charm_url() as mock_get_charm_url: |
| 936 url, machine, service_data, unit_data = app.check_environment( |
| 937 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 938 check_preexisting) |
| 939 # There is no need to call the charmword API if the charm URL is |
| 940 # provided by the user. |
| 941 self.assertFalse(mock_get_charm_url.called) |
| 942 # The provided charm URL has been correctly returned. |
| 943 self.assertEqual(charm_url, url) |
| 944 # Since the provided charm series is trusty, the charm itself can be |
| 945 # safely deployed to machine 0. |
| 946 self.assertEqual('0', machine) |
| 947 # Ensure the function output makes sense. |
| 948 self.assertEqual(2, mock_print.call_count) |
| 949 mock_print.assert_has_calls([ |
| 950 mock.call('bootstrap node series: trusty'), |
| 951 mock.call('charm URL: cs:~juju-gui/trusty/juju-gui-100'), |
| 952 ]) |
| 953 |
| 954 def test_charm_url_provided_series_not_supported(self, mock_print): |
| 955 # The function knows when a custom charm URL cannot be deployed in the |
| 956 # bootstrap node. |
| 957 env = self.make_env() |
| 958 charm_url = 'cs:~juju-gui/trusty/juju-gui-100' |
| 959 env_type = 'ec2' |
| 960 bootstrap_node_series = 'precise' |
| 961 check_preexisting = False |
| 962 with self.patch_get_charm_url() as mock_get_charm_url: |
| 963 url, machine, service_data, unit_data = app.check_environment( |
| 964 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 965 check_preexisting) |
| 966 # There is no need to call the charmword API if the charm URL is |
| 967 # provided by the user. |
| 968 self.assertFalse(mock_get_charm_url.called) |
| 969 # The provided charm URL has been correctly returned. |
| 970 self.assertEqual(charm_url, url) |
| 971 # Since the provided charm series is not precise, the charm must be |
| 972 # deployed to a new machine. |
| 973 self.assertIsNone(machine) |
| 974 # Ensure the function output makes sense. |
| 975 self.assertEqual(2, mock_print.call_count) |
| 976 mock_print.assert_has_calls([ |
| 977 mock.call('bootstrap node series: precise'), |
| 978 mock.call('charm URL: cs:~juju-gui/trusty/juju-gui-100'), |
| 979 ]) |
| 980 |
| 981 def test_status_error(self, mock_print): |
| 982 # A ProgramExit is raised if an error occurs in the status API call. |
| 983 env = self.make_env(side_effect=self.make_env_error('bad wolf')) |
| 984 charm_url = None |
| 985 env_type = 'ec2' |
| 986 bootstrap_node_series = 'trusty' |
| 987 check_preexisting = True |
| 988 with self.assert_program_exit('bad API response: bad wolf'): |
| 989 app.check_environment( |
| 990 env, 'my-gui', charm_url, env_type, bootstrap_node_series, |
| 991 check_preexisting) |
| 992 env.get_status.assert_called_once_with() |
| 993 |
| 994 |
| 995 @helpers.mock_print |
728 class TestDeployGui( | 996 class TestDeployGui( |
729 ProgramExitTestsMixin, helpers.WatcherDataTestsMixin, | 997 ProgramExitTestsMixin, helpers.WatcherDataTestsMixin, |
730 unittest.TestCase): | 998 unittest.TestCase): |
731 | 999 |
732 charm_url = 'cs:precise/juju-gui-100' | 1000 charm_url = 'cs:trusty/juju-gui-42' |
733 | 1001 |
734 def make_env(self, unit_name=None, service_data=None, unit_data=None): | 1002 def make_env(self, unit_name=None): |
735 """Create and return a mock environment object. | 1003 """Create and return a mock environment object. |
736 | 1004 |
737 Set up the object so that a call to add_unit returns the given | 1005 Set up the mock object so that a call to env.add_unit returns the given |
738 unit_name, and a call to status returns a status object containing the | 1006 unit_name. |
739 service and unit described by the given service_data and unit_data. | |
740 """ | 1007 """ |
741 env = mock.Mock() | 1008 env = mock.Mock() |
742 # Set up the add_unit return value. | 1009 # Set up the add_unit return value. |
743 if unit_name is not None: | 1010 if unit_name is not None: |
744 env.add_unit.return_value = {'Units': [unit_name]} | 1011 env.add_unit.return_value = {'Units': [unit_name]} |
745 # Set up the get_status return value. | |
746 status = [] | |
747 if service_data is not None: | |
748 status.append(self.make_service_change(data=service_data)) | |
749 if unit_data is not None: | |
750 status.append(self.make_unit_change(data=unit_data)) | |
751 env.get_status.return_value = status | |
752 return env | 1012 return env |
753 | 1013 |
754 def patch_get_charm_url(self, side_effect=None): | 1014 def test_deployment(self, mock_print): |
755 """Patch the get_charm_url helper function.""" | 1015 # The function correctly deploys and exposes the service in the case |
756 if side_effect is None: | 1016 # the service and its unit are not present in the environment. |
757 side_effect = [self.charm_url] | |
758 mock_get_charm_url = mock.Mock(side_effect=side_effect) | |
759 return mock.patch('quickstart.utils.get_charm_url', mock_get_charm_url) | |
760 | |
761 def check_provided_charm_url( | |
762 self, charm_url, mock_print, expected_logs=None): | |
763 """Ensure the service is deployed and exposed with the given charm URL. | |
764 | |
765 Also check the expected warnings, if they are provided, are logged. | |
766 """ | |
767 env = self.make_env(unit_name='my-gui/42') | 1017 env = self.make_env(unit_name='my-gui/42') |
768 with helpers.assert_logs(expected_logs or [], level='warn'): | 1018 service_data = unit_data = None |
769 app.deploy_gui(env, 'my-gui', '0', charm_url=charm_url) | 1019 unit_name = app.deploy_gui( |
770 env.assert_has_calls([ | 1020 env, 'my-gui', self.charm_url, '0', service_data, unit_data) |
771 mock.call.deploy('my-gui', charm_url, num_units=0), | |
772 mock.call.expose('my-gui'), | |
773 mock.call.add_unit('my-gui', machine_spec='0'), | |
774 ]) | |
775 mock_print.assert_has_calls([ | |
776 mock.call('requesting my-gui deployment'), | |
777 mock.call('charm URL: {}'.format(charm_url)), | |
778 ]) | |
779 | |
780 def check_existing_charm_url( | |
781 self, charm_url, mock_print, expected_logs=None): | |
782 """Ensure the service is correctly found with the given charm URL. | |
783 | |
784 Also check the expected warnings, if they are provided, are logged. | |
785 """ | |
786 service_data = {'CharmURL': charm_url} | |
787 env = self.make_env(unit_name='my-gui/42', service_data=service_data) | |
788 with helpers.assert_logs(expected_logs or [], level='warn'): | |
789 app.deploy_gui(env, 'my-gui', '0', check_preexisting=True) | |
790 env.assert_has_calls([ | |
791 mock.call.get_status(), | |
792 mock.call.add_unit('my-gui', machine_spec='0'), | |
793 ]) | |
794 mock_print.assert_has_calls([ | |
795 mock.call('service my-gui already deployed'), | |
796 mock.call('charm URL: {}'.format(charm_url)), | |
797 ]) | |
798 | |
799 def test_deployment(self, mock_print): | |
800 # The function correctly deploys and exposes the service, retrieving | |
801 # the charm URL from the charmworld API. | |
802 env = self.make_env(unit_name='my-gui/42') | |
803 with self.patch_get_charm_url(): | |
804 unit_name = app.deploy_gui(env, 'my-gui', '0') | |
805 self.assertEqual('my-gui/42', unit_name) | 1021 self.assertEqual('my-gui/42', unit_name) |
806 env.assert_has_calls([ | 1022 env.assert_has_calls([ |
| 1023 # The service has been deployed. |
807 mock.call.deploy('my-gui', self.charm_url, num_units=0), | 1024 mock.call.deploy('my-gui', self.charm_url, num_units=0), |
| 1025 # The service has been exposed. |
808 mock.call.expose('my-gui'), | 1026 mock.call.expose('my-gui'), |
| 1027 # One service unit has been added. |
809 mock.call.add_unit('my-gui', machine_spec='0'), | 1028 mock.call.add_unit('my-gui', machine_spec='0'), |
810 ]) | 1029 ]) |
811 # There is no need to call status if the environment was just created. | 1030 self.assertEqual(5, mock_print.call_count) |
812 self.assertFalse(env.get_status.called) | |
813 mock_print.assert_has_calls([ | 1031 mock_print.assert_has_calls([ |
814 mock.call('requesting my-gui deployment'), | 1032 mock.call('requesting my-gui deployment'), |
815 mock.call('charm URL: {}'.format(self.charm_url)), | |
816 mock.call('my-gui deployment request accepted'), | 1033 mock.call('my-gui deployment request accepted'), |
817 mock.call('exposing service my-gui'), | 1034 mock.call('exposing service my-gui'), |
818 mock.call('requesting new unit deployment'), | 1035 mock.call('requesting new unit deployment'), |
819 mock.call('my-gui/42 deployment request accepted'), | 1036 mock.call('my-gui/42 deployment request accepted'), |
820 ]) | 1037 ]) |
821 | 1038 |
822 def test_existing_environment_without_entities(self, mock_print): | |
823 # The deployment is processed in an already bootstrapped environment | |
824 # with no relevant entities in it. | |
825 env = self.make_env(unit_name='my-gui/42') | |
826 with self.patch_get_charm_url(): | |
827 unit_name = app.deploy_gui( | |
828 env, 'my-gui', '0', check_preexisting=True) | |
829 self.assertEqual('my-gui/42', unit_name) | |
830 env.assert_has_calls([ | |
831 mock.call.get_status(), | |
832 mock.call.deploy('my-gui', self.charm_url, num_units=0), | |
833 mock.call.expose('my-gui'), | |
834 mock.call.add_unit('my-gui', machine_spec='0'), | |
835 ]) | |
836 | |
837 def test_default_charm_url(self, mock_print): | |
838 # The function correctly deploys and exposes the service, even if it is | |
839 # not able to retrieve the charm URL from the charmworld API. | |
840 env = self.make_env(unit_name='my-gui/42') | |
841 log = 'unable to retrieve the my-gui charm URL from the API: boo!' | |
842 with self.patch_get_charm_url(side_effect=IOError('boo!')): | |
843 # A warning is logged which notifies we are using the default URL. | |
844 with helpers.assert_logs([log], level='warn'): | |
845 app.deploy_gui(env, 'my-gui', '0') | |
846 env.assert_has_calls([ | |
847 mock.call.deploy( | |
848 'my-gui', settings.DEFAULT_CHARM_URL, num_units=0), | |
849 mock.call.expose('my-gui'), | |
850 mock.call.add_unit('my-gui', machine_spec='0'), | |
851 ]) | |
852 mock_print.assert_has_calls([ | |
853 mock.call('requesting my-gui deployment'), | |
854 mock.call('charm URL: {}'.format(settings.DEFAULT_CHARM_URL)), | |
855 ]) | |
856 | |
857 def test_existing_service(self, mock_print): | 1039 def test_existing_service(self, mock_print): |
858 # The deployment is executed reusing an already deployed service. | 1040 # The deployment is executed reusing an already deployed service. |
859 env = self.make_env(unit_name='my-gui/42', service_data={}) | 1041 env = self.make_env(unit_name='my-gui/42') |
| 1042 service_data = self.make_service_data() |
| 1043 unit_data = None |
860 unit_name = app.deploy_gui( | 1044 unit_name = app.deploy_gui( |
861 env, 'my-gui', '0', check_preexisting=True) | 1045 env, 'my-gui', self.charm_url, '0', service_data, unit_data) |
862 self.assertEqual('my-gui/42', unit_name) | 1046 self.assertEqual('my-gui/42', unit_name) |
863 env.assert_has_calls([ | 1047 # One service unit has been added. |
864 mock.call.get_status(), | 1048 env.add_unit.assert_called_once_with('my-gui', machine_spec='0') |
865 mock.call.add_unit('my-gui', machine_spec='0'), | |
866 ]) | |
867 # The service is not re-deployed. | 1049 # The service is not re-deployed. |
868 self.assertFalse(env.deploy.called) | 1050 self.assertFalse(env.deploy.called) |
869 # The service is not re-exposed. | 1051 # The service is not re-exposed. |
870 self.assertFalse(env.expose.called) | 1052 self.assertFalse(env.expose.called) |
| 1053 self.assertEqual(3, mock_print.call_count) |
871 mock_print.assert_has_calls([ | 1054 mock_print.assert_has_calls([ |
872 mock.call('service my-gui already deployed'), | 1055 mock.call('service my-gui already deployed'), |
873 mock.call('charm URL: cs:precise/juju-gui-47'), | |
874 mock.call('requesting new unit deployment'), | 1056 mock.call('requesting new unit deployment'), |
875 mock.call('my-gui/42 deployment request accepted'), | 1057 mock.call('my-gui/42 deployment request accepted'), |
876 ]) | 1058 ]) |
877 | 1059 |
878 def test_existing_service_unexposed(self, mock_print): | 1060 def test_existing_service_unexposed(self, mock_print): |
879 # The existing service is exposed if required. | 1061 # The existing service is exposed if required. |
880 service_data = {'Exposed': False} | 1062 env = self.make_env(unit_name='my-gui/42') |
881 env = self.make_env(unit_name='my-gui/42', service_data=service_data) | 1063 service_data = self.make_service_data({'Exposed': False}) |
| 1064 unit_data = None |
882 unit_name = app.deploy_gui( | 1065 unit_name = app.deploy_gui( |
883 env, 'my-gui', '1', check_preexisting=True) | 1066 env, 'my-gui', self.charm_url, '1', service_data, unit_data) |
884 self.assertEqual('my-gui/42', unit_name) | 1067 self.assertEqual('my-gui/42', unit_name) |
885 env.assert_has_calls([ | 1068 env.assert_has_calls([ |
886 mock.call.get_status(), | 1069 # The service has been exposed. |
887 mock.call.expose('my-gui'), | 1070 mock.call.expose('my-gui'), |
| 1071 # One service unit has been added. |
888 mock.call.add_unit('my-gui', machine_spec='1'), | 1072 mock.call.add_unit('my-gui', machine_spec='1'), |
889 ]) | 1073 ]) |
890 # The service is not re-deployed. | 1074 # The service is not re-deployed. |
891 self.assertFalse(env.deploy.called) | 1075 self.assertFalse(env.deploy.called) |
| 1076 self.assertEqual(4, mock_print.call_count) |
892 mock_print.assert_has_calls([ | 1077 mock_print.assert_has_calls([ |
893 mock.call('service my-gui already deployed'), | 1078 mock.call('service my-gui already deployed'), |
894 mock.call('charm URL: cs:precise/juju-gui-47'), | |
895 mock.call('exposing service my-gui'), | 1079 mock.call('exposing service my-gui'), |
896 mock.call('requesting new unit deployment'), | 1080 mock.call('requesting new unit deployment'), |
897 mock.call('my-gui/42 deployment request accepted'), | 1081 mock.call('my-gui/42 deployment request accepted'), |
898 ]) | 1082 ]) |
899 | 1083 |
900 def test_existing_service_and_unit(self, mock_print): | 1084 def test_existing_service_and_unit(self, mock_print): |
901 # A unit is reused if a suitable one is already present. | 1085 # A unit is reused if a suitable one is already present. |
902 env = self.make_env(service_data={}, unit_data={}) | 1086 env = self.make_env() |
| 1087 service_data = self.make_service_data() |
| 1088 unit_data = self.make_unit_data() |
903 unit_name = app.deploy_gui( | 1089 unit_name = app.deploy_gui( |
904 env, 'my-gui', '0', check_preexisting=True) | 1090 env, 'my-gui', self.charm_url, '0', service_data, unit_data) |
905 self.assertEqual('my-gui/47', unit_name) | 1091 self.assertEqual('my-gui/47', unit_name) |
906 env.get_status.assert_called_once_with() | |
907 # The service is not re-deployed. | 1092 # The service is not re-deployed. |
908 self.assertFalse(env.deploy.called) | 1093 self.assertFalse(env.deploy.called) |
909 # The service is not re-exposed. | 1094 # The service is not re-exposed. |
910 self.assertFalse(env.expose.called) | 1095 self.assertFalse(env.expose.called) |
911 # The unit is not re-added. | 1096 # The unit is not re-added. |
912 self.assertFalse(env.add_unit.called) | 1097 self.assertFalse(env.add_unit.called) |
| 1098 self.assertEqual(2, mock_print.call_count) |
913 mock_print.assert_has_calls([ | 1099 mock_print.assert_has_calls([ |
914 mock.call('service my-gui already deployed'), | 1100 mock.call('service my-gui already deployed'), |
915 mock.call('charm URL: cs:precise/juju-gui-47'), | |
916 mock.call('reusing unit my-gui/47'), | 1101 mock.call('reusing unit my-gui/47'), |
917 ]) | 1102 ]) |
918 | 1103 |
919 def test_new_machine(self, mock_print): | 1104 def test_new_machine(self, mock_print): |
920 # The unit is correctly deployed in a new machine. | 1105 # The unit is correctly deployed in a new machine. |
921 env = self.make_env(unit_name='my-gui/42') | 1106 env = self.make_env(unit_name='my-gui/42') |
922 with self.patch_get_charm_url(): | 1107 service_data = unit_data = None |
923 unit_name = app.deploy_gui(env, 'my-gui', None) | 1108 unit_name = app.deploy_gui( |
| 1109 env, 'my-gui', self.charm_url, None, service_data, unit_data) |
924 self.assertEqual('my-gui/42', unit_name) | 1110 self.assertEqual('my-gui/42', unit_name) |
925 env.assert_has_calls([ | 1111 env.assert_has_calls([ |
| 1112 # The service has been deployed. |
926 mock.call.deploy('my-gui', self.charm_url, num_units=0), | 1113 mock.call.deploy('my-gui', self.charm_url, num_units=0), |
| 1114 # The service has been exposed. |
927 mock.call.expose('my-gui'), | 1115 mock.call.expose('my-gui'), |
| 1116 # One service unit has been added to a new machine. |
928 mock.call.add_unit('my-gui', machine_spec=None), | 1117 mock.call.add_unit('my-gui', machine_spec=None), |
929 ]) | 1118 ]) |
930 | 1119 |
931 def test_offical_charm_url_provided(self, mock_print): | |
932 # The function correctly deploys and exposes the service using a user | |
933 # provided revision of the Juju GUI charm URL. | |
934 self.check_provided_charm_url('cs:precise/juju-gui-4242', mock_print) | |
935 | |
936 def test_customized_charm_url_provided(self, mock_print): | |
937 # A customized charm URL is correctly recognized and logged if provided | |
938 # by the user. | |
939 self.check_provided_charm_url( | |
940 'cs:~juju-gui/precise/juju-gui-42', mock_print, | |
941 expected_logs=['using a customized juju-gui charm']) | |
942 | |
943 def test_outdated_charm_url_provided(self, mock_print): | |
944 # An outdated charm URL is correctly recognized and logged if provided | |
945 # by the user. | |
946 self.check_provided_charm_url( | |
947 'cs:precise/juju-gui-1', mock_print, | |
948 expected_logs=[ | |
949 'charm is outdated and may not support bundle deployments']) | |
950 | |
951 def test_unexpected_charm_url_provided(self, mock_print): | |
952 # An unexpected charm URL is correctly recognized and logged if | |
953 # provided by the user. | |
954 self.check_provided_charm_url( | |
955 'cs:precise/exterminate-the-gui-666', mock_print, | |
956 expected_logs=[ | |
957 'unexpected URL for the juju-gui charm: ' | |
958 'the service may not work as expected']) | |
959 | |
960 def test_offical_charm_url_existing(self, mock_print): | |
961 # An existing official charm URL is correctly found. | |
962 self.check_existing_charm_url('cs:precise/juju-gui-4242', mock_print) | |
963 | |
964 def test_customized_charm_url_existing(self, mock_print): | |
965 # An existing customized charm URL is correctly found and logged. | |
966 self.check_existing_charm_url( | |
967 'cs:~juju-gui/precise/juju-gui-42', mock_print, | |
968 expected_logs=['using a customized juju-gui charm']) | |
969 | |
970 def test_outdated_charm_url_existing(self, mock_print): | |
971 # An existing but outdated charm URL is correctly found and logged. | |
972 self.check_existing_charm_url( | |
973 'cs:precise/juju-gui-1', mock_print, | |
974 expected_logs=[ | |
975 'charm is outdated and may not support bundle deployments']) | |
976 | |
977 def test_unexpected_charm_url_existing(self, mock_print): | |
978 # An existing but unexpected charm URL is correctly found and logged. | |
979 self.check_existing_charm_url( | |
980 'cs:precise/exterminate-the-gui-666', mock_print, | |
981 expected_logs=[ | |
982 'unexpected URL for the juju-gui charm: ' | |
983 'the service may not work as expected']) | |
984 | |
985 def test_status_error(self, mock_print): | |
986 # A ProgramExit is raised if an error occurs in the status API call. | |
987 env = self.make_env() | |
988 env.get_status.side_effect = self.make_env_error('bad wolf') | |
989 with self.assert_program_exit('bad API response: bad wolf'): | |
990 app.deploy_gui( | |
991 env, 'another-gui', '0', check_preexisting=True) | |
992 env.get_status.assert_called_once_with() | |
993 | |
994 def test_deploy_error(self, mock_print): | 1120 def test_deploy_error(self, mock_print): |
995 # A ProgramExit is raised if an error occurs in the deploy API call. | 1121 # A ProgramExit is raised if an error occurs in the deploy API call. |
996 env = self.make_env() | 1122 env = self.make_env() |
997 env.deploy.side_effect = self.make_env_error('bad wolf') | 1123 env.deploy.side_effect = self.make_env_error('bad wolf') |
998 with self.patch_get_charm_url(): | 1124 service_data = unit_data = None |
999 with self.assert_program_exit('bad API response: bad wolf'): | 1125 with self.assert_program_exit('bad API response: bad wolf'): |
1000 app.deploy_gui(env, 'another-gui', '0') | 1126 app.deploy_gui( |
| 1127 env, 'another-gui', self.charm_url, '0', |
| 1128 service_data, unit_data) |
1001 env.deploy.assert_called_once_with( | 1129 env.deploy.assert_called_once_with( |
1002 'another-gui', self.charm_url, num_units=0) | 1130 'another-gui', self.charm_url, num_units=0) |
1003 | 1131 |
1004 def test_expose_error(self, mock_print): | 1132 def test_expose_error(self, mock_print): |
1005 # A ProgramExit is raised if an error occurs in the expose API call. | 1133 # A ProgramExit is raised if an error occurs in the expose API call. |
1006 env = self.make_env() | 1134 env = self.make_env() |
1007 env.expose.side_effect = self.make_env_error('bad wolf') | 1135 env.expose.side_effect = self.make_env_error('bad wolf') |
1008 with self.patch_get_charm_url(): | 1136 service_data = unit_data = None |
1009 with self.assert_program_exit('bad API response: bad wolf'): | 1137 with self.assert_program_exit('bad API response: bad wolf'): |
1010 app.deploy_gui(env, 'another-gui', '0') | 1138 app.deploy_gui( |
| 1139 env, 'another-gui', self.charm_url, '0', |
| 1140 service_data, unit_data) |
1011 env.expose.assert_called_once_with('another-gui') | 1141 env.expose.assert_called_once_with('another-gui') |
1012 | 1142 |
1013 def test_add_unit_error(self, mock_print): | 1143 def test_add_unit_error(self, mock_print): |
1014 # A ProgramExit is raised if an error occurs in the add_unit API call. | 1144 # A ProgramExit is raised if an error occurs in the add_unit API call. |
1015 env = self.make_env() | 1145 env = self.make_env() |
1016 env.add_unit.side_effect = self.make_env_error('bad wolf') | 1146 env.add_unit.side_effect = self.make_env_error('bad wolf') |
1017 with self.patch_get_charm_url(): | 1147 service_data = unit_data = None |
1018 with self.assert_program_exit('bad API response: bad wolf'): | 1148 with self.assert_program_exit('bad API response: bad wolf'): |
1019 app.deploy_gui(env, 'another-gui', '0') | 1149 app.deploy_gui( |
| 1150 env, 'another-gui', self.charm_url, '0', |
| 1151 service_data, unit_data) |
1020 env.add_unit.assert_called_once_with('another-gui', machine_spec='0') | 1152 env.add_unit.assert_called_once_with('another-gui', machine_spec='0') |
1021 | 1153 |
1022 def test_other_errors(self, mock_print): | 1154 def test_other_errors(self, mock_print): |
1023 # Any other errors occurred during the process are not trapped. | 1155 # Any other errors occurred during the process are not trapped. |
1024 error = ValueError('explode!') | 1156 error = ValueError('explode!') |
1025 env = self.make_env(unit_name='my-gui/42') | 1157 env = self.make_env(unit_name='my-gui/42') |
1026 env.expose.side_effect = error | 1158 env.expose.side_effect = error |
1027 with self.patch_get_charm_url(): | 1159 service_data = unit_data = None |
1028 with self.assertRaises(ValueError) as context_manager: | 1160 with self.assertRaises(ValueError) as context_manager: |
1029 app.deploy_gui(env, 'juju-gui', '0') | 1161 app.deploy_gui( |
| 1162 env, 'juju-gui', self.charm_url, '0', |
| 1163 service_data, unit_data) |
1030 env.deploy.assert_called_once_with( | 1164 env.deploy.assert_called_once_with( |
1031 'juju-gui', self.charm_url, num_units=0) | 1165 'juju-gui', self.charm_url, num_units=0) |
1032 env.expose.assert_called_once_with('juju-gui') | 1166 env.expose.assert_called_once_with('juju-gui') |
1033 self.assertIs(error, context_manager.exception) | 1167 self.assertIs(error, context_manager.exception) |
1034 | 1168 |
1035 | 1169 |
1036 @helpers.mock_print | 1170 @helpers.mock_print |
1037 class TestWatch( | 1171 class TestWatch( |
1038 ProgramExitTestsMixin, helpers.ValueErrorTestsMixin, | 1172 ProgramExitTestsMixin, helpers.ValueErrorTestsMixin, |
1039 unittest.TestCase): | 1173 unittest.TestCase): |
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1429 app.deploy_bundle(env, self.yaml, self.name, self.bundle_id) | 1563 app.deploy_bundle(env, self.yaml, self.name, self.bundle_id) |
1430 | 1564 |
1431 def test_other_errors(self): | 1565 def test_other_errors(self): |
1432 # Any other errors occurred during the process are not trapped. | 1566 # Any other errors occurred during the process are not trapped. |
1433 env = mock.Mock() | 1567 env = mock.Mock() |
1434 error = ValueError('explode!') | 1568 error = ValueError('explode!') |
1435 env.deploy_bundle.side_effect = error | 1569 env.deploy_bundle.side_effect = error |
1436 with self.assertRaises(ValueError) as context_manager: | 1570 with self.assertRaises(ValueError) as context_manager: |
1437 app.deploy_bundle(env, self.yaml, self.name, None) | 1571 app.deploy_bundle(env, self.yaml, self.name, None) |
1438 self.assertIs(error, context_manager.exception) | 1572 self.assertIs(error, context_manager.exception) |
OLD | NEW |