457
459
class TestCheckBootstrapped(helpers.JenvFileTestsMixin, unittest.TestCase):
459
461
def test_no_jenv_file(self):
460
# A None API URL is returned if the jenv file is not present.
462
# A None API address is returned if the jenv file is not present.
461
463
with self.make_jenv('ec2', ''):
462
464
with helpers.assert_logs([], level='warn'):
463
api_url = app.check_bootstrapped('hp')
464
self.assertIsNone(api_url)
465
api_address = app.check_bootstrapped('hp')
466
self.assertIsNone(api_address)
466
468
def test_invalid_jenv_file(self):
467
# A None API URL is returned if the list of API addresses cannot be
469
# A None API address is returned if the list of API addresses cannot be
468
470
# retrieved from the jenv file.
469
471
with self.make_jenv('ec2', '') as path:
471
'cannot retrieve the Juju API URL: '
473
'cannot retrieve the Juju API address: '
472
474
'cannot read {}: invalid YAML contents: '
473
475
'state-servers key not found in the root section'.format(path)
475
477
with helpers.assert_logs(logs, level='warn'):
476
api_url = app.check_bootstrapped('ec2')
477
self.assertIsNone(api_url)
478
api_address = app.check_bootstrapped('ec2')
479
self.assertIsNone(api_address)
479
481
def test_no_api_addresses(self):
480
# A None API URL is returned if the list of API addresses is empty.
482
# A None API address is returned if the list of API addresses is empty.
481
483
jenv_data = {'state-servers': []}
482
logs = ['cannot retrieve the Juju API URL: no addresses found']
484
logs = ['cannot retrieve the Juju API address: no addresses found']
483
485
with self.make_jenv('local', yaml.safe_dump(jenv_data)):
484
486
with helpers.assert_logs(logs, level='warn'):
485
api_url = app.check_bootstrapped('local')
486
self.assertIsNone(api_url)
487
api_address = app.check_bootstrapped('local')
488
self.assertIsNone(api_address)
488
490
def test_api_address_not_listening(self):
489
# A None API URL is returned if there is no reachable API address.
491
# A None API address is returned if there is no reachable API address.
491
'cannot retrieve the Juju API URL: '
493
'cannot retrieve the Juju API address: '
492
494
'cannot connect to any of the following addresses: '
493
495
'localhost:17070, 10.0.3.1:17070'
495
497
with self.make_jenv('local', yaml.safe_dump(self.jenv_data)):
496
498
with helpers.assert_logs(logs, level='warn'):
497
499
with helpers.patch_socket_create_connection('bad wolf'):
498
api_url = app.check_bootstrapped('local')
499
self.assertIsNone(api_url)
500
api_address = app.check_bootstrapped('local')
501
self.assertIsNone(api_address)
501
503
def test_bootstrapped(self):
502
# The first listening API URL is returned if the environment is already
504
# The first listening API address is returned if the environment is
505
# already bootstrapped.
504
506
with self.make_jenv('hp', yaml.safe_dump(self.jenv_data)):
505
507
with helpers.assert_logs([], level='warn'):
506
508
with helpers.patch_socket_create_connection():
507
api_url = app.check_bootstrapped('hp')
509
api_address = app.check_bootstrapped('hp')
508
510
# The first API address is returned.
509
self.assertEqual('wss://localhost:17070', api_url)
511
self.assertEqual('localhost:17070', api_address)
512
514
class TestBootstrap(
700
702
mock_call.assert_has_calls(expected_calls)
705
class TestGetEnvUuidOrNone(
706
helpers.JenvFileTestsMixin, ProgramExitTestsMixin, unittest.TestCase):
708
def test_success(self):
709
# The environment UUID is successfully retrieved.
710
with self.make_jenv('ec2', yaml.safe_dump(self.jenv_data)):
711
env_uuid = app.get_env_uuid_or_none('ec2')
712
self.assertEqual('__unique_identifier__', env_uuid)
714
def test_no_uuid(self):
715
# None is returned if the environment UUID is not found.
716
data = {'user': 'jean-luc', 'password': 'Secret!'}
717
with self.make_jenv('ec2', yaml.safe_dump(data)):
718
env_uuid = app.get_env_uuid_or_none('ec2')
719
self.assertIsNone(env_uuid)
721
def test_error(self):
722
# A ProgramExit is raised if the environment UUID cannot be retrieved.
723
with self.make_jenv('ec2', '') as path:
726
'cannot retrieve environment unique identifier: unable to '
727
"open file {}: [Errno 2] No such file or directory: '{}'"
728
''.format(path, path))
729
with self.assert_program_exit(expected_error):
730
app.get_env_uuid_or_none('ec2')
703
733
class TestGetCredentials(
704
734
helpers.JenvFileTestsMixin, ProgramExitTestsMixin, unittest.TestCase):
722
752
app.get_credentials('ec2')
755
class TestGetApiAddress(
726
756
helpers.CallTestsMixin, ProgramExitTestsMixin, unittest.TestCase):
729
759
juju_command = settings.JUJU_CMD_PATHS['default']
731
761
def test_success(self):
732
# The API URL is correctly returned.
762
# The API address is correctly returned.
733
763
api_addresses = json.dumps(['api.example.com:17070', 'not-today'])
734
764
with self.patch_call(retcode=0, output=api_addresses) as mock_call:
735
api_url = app.get_api_url(self.env_name, self.juju_command)
736
self.assertEqual('wss://api.example.com:17070', api_url)
765
api_address = app.get_api_address(self.env_name, self.juju_command)
766
self.assertEqual('api.example.com:17070', api_address)
737
767
mock_call.assert_called_once_with(
738
768
self.juju_command, 'api-endpoints', '-e', self.env_name,
739
769
'--format', 'json')
741
771
def test_failure(self):
742
# A ProgramExit is raised if an error occurs retrieving the API URL.
772
# A ProgramExit is raised if an error occurs retrieving the address.
743
773
with self.patch_call(retcode=1, error='bad wolf') as mock_call:
744
774
with self.assert_program_exit('bad wolf'):
745
app.get_api_url(self.env_name, self.juju_command)
775
app.get_api_address(self.env_name, self.juju_command)
746
776
mock_call.assert_called_once_with(
747
777
self.juju_command, 'api-endpoints', '-e', self.env_name,
748
778
'--format', 'json')
904
934
return mock.patch(
905
935
'quickstart.netutils.get_charm_url', mock_get_charm_url)
937
def assert_charm_equal(self, expected_url, charm):
938
"""Ensure the given charm has the expected URL."""
939
expected_charm = charms.Charm.from_url(expected_url)
940
self.assertEqual(expected_charm, charm)
907
942
def test_environment_just_bootstrapped(self, mock_print):
908
943
# The function correctly retrieves the charm URL and machine, and
909
944
# handles the case when the charm URL is not provided by the user.
917
952
check_preexisting = False
918
953
with self.patch_get_charm_url(
919
954
return_value='cs:trusty/juju-gui-42') as mock_get_charm_url:
920
url, machine, service_data, unit_data = app.check_environment(
955
charm, machine, service_data, unit_data = app.check_environment(
921
956
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
922
957
check_preexisting)
923
958
# There is no need to call status if the environment was just created.
924
959
self.assertFalse(env.get_status.called)
925
960
# The charm URL has been retrieved from the charm store API based on
926
961
# the current bootstrap node series.
927
self.assertEqual('cs:trusty/juju-gui-42', url)
962
self.assert_charm_equal('cs:trusty/juju-gui-42', charm)
928
963
mock_get_charm_url.assert_called_once_with(bootstrap_node_series)
929
964
# Since the bootstrap node series is supported by the GUI charm, the
930
965
# GUI unit can be deployed to machine 0.
952
987
check_preexisting = True
953
988
with self.patch_get_charm_url(
954
989
return_value='cs:precise/juju-gui-42') as mock_get_charm_url:
955
url, machine, service_data, unit_data = app.check_environment(
990
charm, machine, service_data, unit_data = app.check_environment(
956
991
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
957
992
check_preexisting)
958
993
# The environment status has been retrieved.
959
994
env.get_status.assert_called_once_with()
960
995
# The charm URL has been retrieved from the charm store API based on
961
996
# the current bootstrap node series.
962
self.assertEqual('cs:precise/juju-gui-42', url)
997
self.assert_charm_equal('cs:precise/juju-gui-42', charm)
963
998
mock_get_charm_url.assert_called_once_with(bootstrap_node_series)
964
999
# Since the bootstrap node series is supported by the GUI charm, the
965
1000
# GUI unit can be deployed to machine 0.
984
1019
bootstrap_node_series = 'precise'
985
1020
check_preexisting = True
986
1021
with self.patch_get_charm_url() as mock_get_charm_url:
987
url, machine, service_data, unit_data = app.check_environment(
1022
charm, machine, service_data, unit_data = app.check_environment(
988
1023
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
989
1024
check_preexisting)
990
1025
# The environment status has been retrieved.
991
1026
env.get_status.assert_called_once_with()
992
1027
# The charm URL has been retrieved from the environment.
993
self.assertEqual('cs:precise/juju-gui-47', url)
1028
self.assert_charm_equal('cs:precise/juju-gui-47', charm)
994
1029
self.assertFalse(mock_get_charm_url.called)
995
1030
# Since the bootstrap node series is supported by the GUI charm, the
996
1031
# GUI unit can be safely deployed to machine 0.
1009
1044
check_preexisting = False
1010
1045
with self.patch_get_charm_url(
1011
1046
return_value='cs:trusty/juju-gui-42') as mock_get_charm_url:
1012
url, machine, service_data, unit_data = app.check_environment(
1047
charm, machine, service_data, unit_data = app.check_environment(
1013
1048
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
1014
1049
check_preexisting)
1015
1050
# The charm URL has been retrieved from the charm store API using the
1016
1051
# most recent supported series.
1017
self.assertEqual('cs:trusty/juju-gui-42', url)
1052
self.assert_charm_equal('cs:trusty/juju-gui-42', charm)
1018
1053
mock_get_charm_url.assert_called_once_with('trusty')
1019
1054
# The Juju GUI unit cannot be deployed to saucy machine 0.
1020
1055
self.assertIsNone(machine)
1034
1069
bootstrap_node_series = 'trusty'
1035
1070
check_preexisting = False
1036
1071
with self.patch_get_charm_url(return_value='cs:trusty/juju-gui-42'):
1037
url, machine, service_data, unit_data = app.check_environment(
1072
charm, machine, service_data, unit_data = app.check_environment(
1038
1073
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
1039
1074
check_preexisting)
1040
1075
# The charm URL has been correctly retrieved from the charm store API.
1041
self.assertEqual('cs:trusty/juju-gui-42', url)
1076
self.assert_charm_equal('cs:trusty/juju-gui-42', charm)
1042
1077
# The Juju GUI unit cannot be deployed to localhost.
1043
1078
self.assertIsNone(machine)
1051
1086
bootstrap_node_series = 'trusty'
1052
1087
check_preexisting = False
1053
1088
with self.patch_get_charm_url(return_value='cs:trusty/juju-gui-42'):
1054
url, machine, service_data, unit_data = app.check_environment(
1089
_, machine, _, _ = app.check_environment(
1055
1090
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
1056
1091
check_preexisting)
1057
1092
self.assertIsNone(machine)
1065
1100
bootstrap_node_series = 'precise'
1066
1101
check_preexisting = False
1067
1102
with self.patch_get_charm_url(side_effect=IOError('boo!')):
1068
url, machine, service_data, unit_data = app.check_environment(
1103
charm, machine, service_data, unit_data = app.check_environment(
1069
1104
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
1070
1105
check_preexisting)
1071
1106
# The default charm URL for the given series is returned.
1072
self.assertEqual(settings.DEFAULT_CHARM_URLS['precise'], url)
1107
self.assert_charm_equal(settings.DEFAULT_CHARM_URLS['precise'], charm)
1073
1108
self.assertEqual('0', machine)
1075
1110
def test_most_recent_default_charm_url(self, mock_print):
1082
1117
bootstrap_node_series = 'saucy'
1083
1118
check_preexisting = False
1084
1119
with self.patch_get_charm_url(side_effect=IOError('boo!')):
1085
url, machine, service_data, unit_data = app.check_environment(
1120
charm, machine, service_data, unit_data = app.check_environment(
1086
1121
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
1087
1122
check_preexisting)
1088
1123
# The default charm URL for the given series is returned.
1089
1124
series = settings.JUJU_GUI_SUPPORTED_SERIES[-1]
1090
self.assertEqual(settings.DEFAULT_CHARM_URLS[series], url)
1125
self.assert_charm_equal(settings.DEFAULT_CHARM_URLS[series], charm)
1091
1126
self.assertIsNone(machine)
1093
1128
def test_charm_url_provided(self, mock_print):
1099
1134
bootstrap_node_series = 'trusty'
1100
1135
check_preexisting = False
1101
1136
with self.patch_get_charm_url() as mock_get_charm_url:
1102
url, machine, service_data, unit_data = app.check_environment(
1137
charm, machine, service_data, unit_data = app.check_environment(
1103
1138
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
1104
1139
check_preexisting)
1105
1140
# There is no need to call the charmword API if the charm URL is
1106
1141
# provided by the user.
1107
1142
self.assertFalse(mock_get_charm_url.called)
1108
1143
# The provided charm URL has been correctly returned.
1109
self.assertEqual(charm_url, url)
1144
self.assert_charm_equal(charm_url, charm)
1110
1145
# Since the provided charm series is trusty, the charm itself can be
1111
1146
# safely deployed to machine 0.
1112
1147
self.assertEqual('0', machine)
1126
1161
bootstrap_node_series = 'precise'
1127
1162
check_preexisting = False
1128
1163
with self.patch_get_charm_url() as mock_get_charm_url:
1129
url, machine, service_data, unit_data = app.check_environment(
1164
charm, machine, service_data, unit_data = app.check_environment(
1130
1165
env, 'my-gui', charm_url, env_type, bootstrap_node_series,
1131
1166
check_preexisting)
1132
1167
# There is no need to call the charmword API if the charm URL is
1133
1168
# provided by the user.
1134
1169
self.assertFalse(mock_get_charm_url.called)
1135
1170
# The provided charm URL has been correctly returned.
1136
self.assertEqual(charm_url, url)
1171
self.assert_charm_equal(charm_url, charm)
1137
1172
# Since the provided charm series is not precise, the charm must be
1138
1173
# deployed to a new machine.
1139
1174
self.assertIsNone(machine)