~hloeung/mojo/force-destroy-model

« back to all changes in this revision

Viewing changes to mojo/tests/test_juju2.py

  • Committer: mergebot at canonical
  • Author(s): "Stuart Bishop"
  • Date: 2020-04-22 13:04:02 UTC
  • mfrom: (545.1.6 blacken)
  • Revision ID: mergebot@juju-139df4-prod-is-toolbox-0.canonical.com-20200422130402-55dc5d92dj2lx14h
Reformat code with black

Reviewed-on: https://code.launchpad.net/~stub/mojo/blacken/+merge/382726
Reviewed-by: Tom Haddon <tom.haddon@canonical.com>

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
    from unittest import mock
19
19
 
20
20
 
21
 
JUJU_PATH, _ = get_command('juju')
 
21
JUJU_PATH, _ = get_command("juju")
22
22
 
23
23
 
24
24
class Juju2StatusTestCase(TestCase):
25
 
 
26
25
    @staticmethod
27
26
    def _testdata_from_file(filename):
28
27
        """ Loads testdata files.
31
30
        Returns: string of testdata.
32
31
        """
33
32
        base_dir = os.path.dirname(os.path.abspath(__file__))
34
 
        with open(os.path.join(base_dir, 'testdata', filename), 'r') as test_file:
 
33
        with open(os.path.join(base_dir, "testdata", filename), "r") as test_file:
35
34
            return test_file.read()
36
35
 
37
36
    def setUp(self):
38
 
        with mock.patch('mojo.juju.status.major_version', new=2):
 
37
        with mock.patch("mojo.juju.status.major_version", new=2):
39
38
            self.status = mojo.juju.status.Juju2Status()
40
 
        self.status._raw_status = self._testdata_from_file('juju2-idle.yaml')
 
39
        self.status._raw_status = self._testdata_from_file("juju2-idle.yaml")
41
40
 
42
41
    def test_machine_ids_list(self):
43
42
        """Test getting all machine IDs"""
44
 
        self.assertEqual(self.status.machine_ids_list(), ['2', '3'])
 
43
        self.assertEqual(self.status.machine_ids_list(), ["2", "3"])
45
44
 
46
 
    @mock.patch('mojo.juju.status.Juju2Status.status')
 
45
    @mock.patch("mojo.juju.status.Juju2Status.status")
47
46
    def test_machine_and_container_ids_list(self, status_mock):
48
47
        """Test getting all machine and container IDs"""
49
48
        # First test on a status that doesn't have containers
50
 
        with mock.patch('mojo.juju.status.major_version', new=2):
 
49
        with mock.patch("mojo.juju.status.major_version", new=2):
51
50
            self.status = mojo.juju.status.Juju2Status()
52
 
        status_mock.return_value = self._testdata_from_file('juju2-idle.yaml')
53
 
        self.assertEqual(self.status.machine_ids_list(), ['2', '3'])
54
 
        self.assertEqual(self.status.machine_and_container_ids_list(), ['2', '3'])
 
51
        status_mock.return_value = self._testdata_from_file("juju2-idle.yaml")
 
52
        self.assertEqual(self.status.machine_ids_list(), ["2", "3"])
 
53
        self.assertEqual(self.status.machine_and_container_ids_list(), ["2", "3"])
55
54
        # And now test on a status that does have containers
56
 
        with mock.patch('mojo.juju.status.major_version', new=2):
 
55
        with mock.patch("mojo.juju.status.major_version", new=2):
57
56
            self.status = mojo.juju.status.Juju2Status()
58
 
        status_mock.return_value = self._testdata_from_file('juju2-openstack-with-containers.yaml')
59
 
        self.assertEqual(self.status.machine_ids_list(), ['0', '1', '2', '3', '4', '5', '6', '7', '8'])
60
 
        self.assertEqual(self.status.machine_and_container_ids_list(),
61
 
                         ['0', '0/lxd/0', '0/lxd/1', '0/lxd/2', '0/lxd/3', '0/lxd/4', '0/lxd/5', '0/lxd/6', '0/lxd/7',
62
 
                          '1', '1/lxd/0', '1/lxd/1', '1/lxd/2', '1/lxd/3', '1/lxd/4', '1/lxd/5', '1/lxd/6', '1/lxd/7',
63
 
                          '2', '3', '4', '5', '6', '7', '8'])
 
57
        status_mock.return_value = self._testdata_from_file("juju2-openstack-with-containers.yaml")
 
58
        self.assertEqual(self.status.machine_ids_list(), ["0", "1", "2", "3", "4", "5", "6", "7", "8"])
 
59
        self.assertEqual(
 
60
            self.status.machine_and_container_ids_list(),
 
61
            [
 
62
                "0",
 
63
                "0/lxd/0",
 
64
                "0/lxd/1",
 
65
                "0/lxd/2",
 
66
                "0/lxd/3",
 
67
                "0/lxd/4",
 
68
                "0/lxd/5",
 
69
                "0/lxd/6",
 
70
                "0/lxd/7",
 
71
                "1",
 
72
                "1/lxd/0",
 
73
                "1/lxd/1",
 
74
                "1/lxd/2",
 
75
                "1/lxd/3",
 
76
                "1/lxd/4",
 
77
                "1/lxd/5",
 
78
                "1/lxd/6",
 
79
                "1/lxd/7",
 
80
                "2",
 
81
                "3",
 
82
                "4",
 
83
                "5",
 
84
                "6",
 
85
                "7",
 
86
                "8",
 
87
            ],
 
88
        )
64
89
 
65
90
    def test_applications_ips(self):
66
91
        """"Test getting services_list"""
67
 
        self.assertEqual(self.status.application_ips('apache2'), ['fd28:938b:9119:802d:216:3eff:fe96:aa2d'])
68
 
        self.assertEqual(self.status.application_ips('prometheus'), ['10.47.34.203'])
 
92
        self.assertEqual(self.status.application_ips("apache2"), ["fd28:938b:9119:802d:216:3eff:fe96:aa2d"])
 
93
        self.assertEqual(self.status.application_ips("prometheus"), ["10.47.34.203"])
69
94
 
70
95
    def test_applications_list(self):
71
96
        """"Test getting services_list"""
72
 
        self.assertEqual(self.status.applications_list(), ['apache2', 'prometheus'])
73
 
        self.assertEqual(self.status.applications_list(exclude_subordinates=False), ['apache2', 'prometheus', 'telegraf'])
 
97
        self.assertEqual(self.status.applications_list(), ["apache2", "prometheus"])
 
98
        self.assertEqual(
 
99
            self.status.applications_list(exclude_subordinates=False), ["apache2", "prometheus", "telegraf"]
 
100
        )
74
101
 
75
102
    def test_application_machine_numbers(self):
76
 
        self.assertEqual(self.status.application_machine_numbers('apache2'), ['3'])
 
103
        self.assertEqual(self.status.application_machine_numbers("apache2"), ["3"])
77
104
 
78
105
    def test_application_units(self):
79
 
        self.assertEqual(self.status.application_units('apache2'), ['apache2/1'])
 
106
        self.assertEqual(self.status.application_units("apache2"), ["apache2/1"])
80
107
 
81
 
    @mock.patch('logging.info')
82
 
    @mock.patch('mojo.juju.status.wait')
83
 
    @mock.patch('mojo.juju.status.Juju2Status.status')
84
 
    @mock.patch('subprocess.check_output')
85
 
    @mock.patch('time.sleep')
 
108
    @mock.patch("logging.info")
 
109
    @mock.patch("mojo.juju.status.wait")
 
110
    @mock.patch("mojo.juju.status.Juju2Status.status")
 
111
    @mock.patch("subprocess.check_output")
 
112
    @mock.patch("time.sleep")
86
113
    def test_check_and_wait_juju_wait(self, _sleep, _check_output, status_mock, _wait, _logging_info):
87
 
        _wait.return_value = ''
 
114
        _wait.return_value = ""
88
115
        # Confirm we don't raise a JujuStatusError
89
 
        with mock.patch('mojo.juju.status.major_version', new=2):
 
116
        with mock.patch("mojo.juju.status.major_version", new=2):
90
117
            self.status = mojo.juju.status.Juju2Status()
91
 
        status_mock.return_value = self._testdata_from_file('juju2-idle.yaml')
 
118
        status_mock.return_value = self._testdata_from_file("juju2-idle.yaml")
92
119
        # First of all call with default (wait_for_steady=False) and confirm
93
120
        # wait not called (because juju status is okay).
94
121
        self.status.check_and_wait()
101
128
        expected_logging_args_list = [
102
129
            "Waiting for environment to reach steady state",
103
130
            "Environment has reached steady state",
104
 
            ]
 
131
        ]
105
132
        self.assertEquals(len(_logging_info.call_args_list), len(expected_logging_args_list))
106
133
        for i, call in enumerate(_logging_info.call_args_list):
107
134
            self.assertEquals(call, mock.call(expected_logging_args_list[i]))
108
135
 
109
 
    @mock.patch('mojo.juju.status.Juju2Status.status')
110
 
    @mock.patch('subprocess.check_output')
111
 
    @mock.patch('time.sleep')
 
136
    @mock.patch("mojo.juju.status.Juju2Status.status")
 
137
    @mock.patch("subprocess.check_output")
 
138
    @mock.patch("time.sleep")
112
139
    def test_check_and_wait(self, _sleep, _check_output, status_mock):
113
140
        # Wait for steady state timeouts
114
 
        with mock.patch('mojo.juju.status.major_version', new=2):
115
 
            self.status = mojo.juju.status.Juju2Status()
116
 
        status_mock.return_value = self._testdata_from_file('juju2-busy.yaml')
117
 
        with self.assertRaises(mojo.juju.JujuWaitException):
118
 
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
119
 
        with mock.patch('mojo.juju.status.major_version', new=2):
120
 
            self.status = mojo.juju.status.Juju2Status()
121
 
        status_mock.return_value = self._testdata_from_file('juju2-busy2.yaml')
122
 
        with self.assertRaises(mojo.juju.JujuWaitException):
123
 
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
124
 
        with mock.patch('mojo.juju.status.major_version', new=2):
125
 
            self.status = mojo.juju.status.Juju2Status()
126
 
        status_mock.return_value = self._testdata_from_file('juju2-subordinate-busy.yaml')
127
 
        with self.assertRaises(mojo.juju.JujuWaitException):
128
 
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
129
 
        status_mock.return_value = self._testdata_from_file('juju2-subordinate-busy2.yaml')
 
141
        with mock.patch("mojo.juju.status.major_version", new=2):
 
142
            self.status = mojo.juju.status.Juju2Status()
 
143
        status_mock.return_value = self._testdata_from_file("juju2-busy.yaml")
 
144
        with self.assertRaises(mojo.juju.JujuWaitException):
 
145
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
 
146
        with mock.patch("mojo.juju.status.major_version", new=2):
 
147
            self.status = mojo.juju.status.Juju2Status()
 
148
        status_mock.return_value = self._testdata_from_file("juju2-busy2.yaml")
 
149
        with self.assertRaises(mojo.juju.JujuWaitException):
 
150
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
 
151
        with mock.patch("mojo.juju.status.major_version", new=2):
 
152
            self.status = mojo.juju.status.Juju2Status()
 
153
        status_mock.return_value = self._testdata_from_file("juju2-subordinate-busy.yaml")
 
154
        with self.assertRaises(mojo.juju.JujuWaitException):
 
155
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
 
156
        status_mock.return_value = self._testdata_from_file("juju2-subordinate-busy2.yaml")
130
157
        with self.assertRaises(mojo.juju.JujuWaitException):
131
158
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
132
159
 
133
160
        # error states
134
 
        with mock.patch('mojo.juju.status.major_version', new=2):
 
161
        with mock.patch("mojo.juju.status.major_version", new=2):
135
162
            self.status = mojo.juju.status.Juju2Status()
136
 
        status_mock.return_value = self._testdata_from_file('juju2-fail.yaml')
 
163
        status_mock.return_value = self._testdata_from_file("juju2-fail.yaml")
137
164
        with self.assertRaises(mojo.juju.JujuStatusError):
138
165
            self.status.check_and_wait()
139
 
        with mock.patch('mojo.juju.status.major_version', new=2):
 
166
        with mock.patch("mojo.juju.status.major_version", new=2):
140
167
            self.status = mojo.juju.status.Juju2Status()
141
 
        status_mock.return_value = self._testdata_from_file('juju2-subordinate-fail.yaml')
 
168
        status_mock.return_value = self._testdata_from_file("juju2-subordinate-fail.yaml")
142
169
        with self.assertRaises(mojo.juju.JujuStatusError):
143
170
            self.status.check_and_wait()
144
171
 
145
172
        # And now confirm we don't raise an error on an environment in error
146
173
        # state if we pass the right additional_error_states
147
 
        with mock.patch('mojo.juju.status.major_version', new=2):
148
 
            self.status = mojo.juju.status.Juju2Status(additional_ready_states=['maintenance', 'executing'])
149
 
        with mock.patch('mojo.juju.status.major_version', new=2):
150
 
            self.status = mojo.juju.status.Juju2Status()
151
 
        self.status._raw_status = self._testdata_from_file('juju2-busy.yaml')
152
 
        with mock.patch('mojo.juju.status.major_version', new=2):
153
 
            self.status = mojo.juju.status.Juju2Status()
154
 
        status_mock.return_value = self._testdata_from_file('juju2-busy.yaml')
155
 
        with mock.patch('mojo.juju.status.wait', new=lambda max_wait: None):
 
174
        with mock.patch("mojo.juju.status.major_version", new=2):
 
175
            self.status = mojo.juju.status.Juju2Status(additional_ready_states=["maintenance", "executing"])
 
176
        with mock.patch("mojo.juju.status.major_version", new=2):
 
177
            self.status = mojo.juju.status.Juju2Status()
 
178
        self.status._raw_status = self._testdata_from_file("juju2-busy.yaml")
 
179
        with mock.patch("mojo.juju.status.major_version", new=2):
 
180
            self.status = mojo.juju.status.Juju2Status()
 
181
        status_mock.return_value = self._testdata_from_file("juju2-busy.yaml")
 
182
        with mock.patch("mojo.juju.status.wait", new=lambda max_wait: None):
156
183
            self.status.check_and_wait(wait_for_steady=True, timeout=1)
157
184
 
158
 
    @mock.patch('subprocess.check_output')
 
185
    @mock.patch("subprocess.check_output")
159
186
    def test_controller_version(self, mock_check_output):
160
 
        with mock.patch('mojo.juju.status.major_version', new=2):
 
187
        with mock.patch("mojo.juju.status.major_version", new=2):
161
188
            self.status = mojo.juju.status.Juju2Status()
162
 
        mock_check_output.return_value = self._testdata_from_file('juju2-controllers.yaml')
163
 
        self.assertEqual('2.0-rc1', self.status.controller_version())
 
189
        mock_check_output.return_value = self._testdata_from_file("juju2-controllers.yaml")
 
190
        self.assertEqual("2.0-rc1", self.status.controller_version())
164
191
 
165
192
    def test_machine_instance_id(self):
166
 
        self.assertEqual(self.status.machine_instance_id('2'), 'juju-fdef5d-2')
 
193
        self.assertEqual(self.status.machine_instance_id("2"), "juju-fdef5d-2")
167
194
 
168
195
    def test_model_version(self):
169
 
        self.assertEqual(self.status.model_version(), '2.0-beta18')
 
196
        self.assertEqual(self.status.model_version(), "2.0-beta18")
170
197
 
171
198
    def test_status(self):
172
199
        """Test the status function"""
173
200
        self.assertEqual(self.status.status(), self.status._raw_status)
174
201
 
175
 
        with mock.patch('subprocess.check_output') as mock_check_output:
 
202
        with mock.patch("subprocess.check_output") as mock_check_output:
176
203
            # First check with no environment set
177
204
            no_env_status = mojo.juju.status.Juju2Status(command_timeout=None)
178
205
            no_env_status.status()
179
 
            mock_check_output.assert_called_with(
180
 
                [JUJU_PATH, 'status', '--format=yaml'], universal_newlines=True)
 
206
            mock_check_output.assert_called_with([JUJU_PATH, "status", "--format=yaml"], universal_newlines=True)
181
207
            # Second test with an environment set
182
208
            env_status = mojo.juju.status.Juju2Status(environment="test-env")
183
209
            env_status.status()
184
210
            mock_check_output.assert_called_with(
185
 
                ['/usr/bin/timeout', '--kill-after', '5', '600',
186
 
                 JUJU_PATH, 'status', '--format=yaml', '-m', 'test-env'], universal_newlines=True)
 
211
                [
 
212
                    "/usr/bin/timeout",
 
213
                    "--kill-after",
 
214
                    "5",
 
215
                    "600",
 
216
                    JUJU_PATH,
 
217
                    "status",
 
218
                    "--format=yaml",
 
219
                    "-m",
 
220
                    "test-env",
 
221
                ],
 
222
                universal_newlines=True,
 
223
            )
187
224
 
188
 
        with mock.patch('subprocess.check_output') as mock_check_output:
 
225
        with mock.patch("subprocess.check_output") as mock_check_output:
189
226
            # Check that if we're calling status() where _raw_status is
190
227
            # set, it doesn't query juju status
191
228
            self.assertTrue(self.status._raw_status)
195
232
            # called
196
233
            self.status.status(force_update=True)
197
234
            mock_check_output.assert_called_with(
198
 
                ['/usr/bin/timeout', '--kill-after', '5', '600',
199
 
                 JUJU_PATH, 'status', '--format=yaml'], universal_newlines=True)
 
235
                ["/usr/bin/timeout", "--kill-after", "5", "600", JUJU_PATH, "status", "--format=yaml"],
 
236
                universal_newlines=True,
 
237
            )
200
238
 
201
 
    @mock.patch('mojo.juju.status.check_output_with_timeout')
 
239
    @mock.patch("mojo.juju.status.check_output_with_timeout")
202
240
    def test_yaml_status(self, _check_output_with_timeout):
203
241
        """Test the yaml_status function"""
204
 
        _check_output_with_timeout.return_value = self._testdata_from_file('juju2-idle.yaml')
 
242
        _check_output_with_timeout.return_value = self._testdata_from_file("juju2-idle.yaml")
205
243
        self.maxDiff = None
206
244
        expected = {
207
 
            'model': {'name': 'mojo-test', 'controller': 'laptop', 'cloud': 'lxd',
208
 
                      'region': 'localhost', 'version': '2.0-beta18'},
209
 
            'machines': {'2': {'juju-status': {'current': 'started', 'since': '16 Sep 2016 14:56:46Z',
210
 
                                               'version': '2.0-beta18'},
211
 
                               'dns-name': '10.47.34.203',
212
 
                               'instance-id': 'juju-fdef5d-2',
213
 
                               'machine-status': {'current': 'running', 'message': 'Running',
214
 
                                                  'since': '16 Sep 2016 14:55:58Z'},
215
 
                               'series': 'xenial',
216
 
                               'hardware': 'arch=amd64 cpu-cores=0 mem=0M'},
217
 
                         '3': {'juju-status': {'current': 'started', 'since': '16 Sep 2016 14:57:11Z',
218
 
                                               'version': '2.0-beta18'},
219
 
                               'dns-name': 'fd28:938b:9119:802d:216:3eff:fe96:aa2d',
220
 
                               'instance-id': 'juju-fdef5d-3',
221
 
                               'machine-status': {'current': 'running', 'message': 'Running',
222
 
                                                  'since': '16 Sep 2016 14:56:19Z'},
223
 
                               'series': 'xenial',
224
 
                               'hardware': 'arch=amd64 cpu-cores=0 mem=0M'}},
225
 
            'applications': {'apache2': {'charm': 'cs:trusty/apache2-20', 'series': 'xenial',
226
 
                                         'os': 'ubuntu', 'charm-origin': 'jujucharms',
227
 
                                         'charm-name': 'apache2', 'charm-rev': 20, 'exposed': False,
228
 
                                         'application-status': {'current': 'unknown',
229
 
                                                                'since': '16 Sep 2016 14:57:53Z'},
230
 
                                         'relations': {'juju-info': ['telegraf']},
231
 
                                         'units': {'apache2/1': {'workload-status': {'current': 'unknown',
232
 
                                                                                     'since': '16 Sep 2016 14:57:53Z'},
233
 
                                                                 'juju-status': {'current': 'idle',
234
 
                                                                                 'since': '16 Sep 2016 15:20:34Z',
235
 
                                                                                 'version': '2.0-beta18'},
236
 
                                                                 'machine': '3',
237
 
                                                                 'public-address': 'fd28:938b:9119:802d:216:3eff:fe96:aa2d',
238
 
                                                                 'subordinates': {'telegraf/0': {
239
 
                                                                        'workload-status': {
240
 
                                                                            'current': 'active',
241
 
                                                                            'message': 'Monitoring apache2/1',
242
 
                                                                            'since': '16 Sep 2016 15:23:55Z'},
243
 
                                                                        'juju-status': {
244
 
                                                                                'current': 'idle',
245
 
                                                                                'since': '16 Sep 2016 15:23:55Z',
246
 
                                                                                'version': '2.0-beta18'},
247
 
                                                                        'upgrading-from': 'cs:~telegraf-charmers/telegraf-6',
248
 
                                                                        'open-ports': ['9103/tcp'],
249
 
                                                                        'public-address': 'fd28:938b:9119:802d:216:3eff:fe96:aa2d'
250
 
                                                                        }}}}},
251
 
                             'prometheus': {'charm': 'cs:~prometheus-charmers/prometheus-2', 'series': 'xenial',
252
 
                                            'os': 'ubuntu', 'charm-origin': 'jujucharms',
253
 
                                            'charm-name': 'prometheus', 'charm-rev': 2, 'exposed': False,
254
 
                                            'application-status': {'current': 'active', 'message': 'Ready',
255
 
                                                                   'since': '16 Sep 2016 14:59:53Z'},
256
 
                                            'relations': {'juju-info': ['telegraf']},
257
 
                                            'units': {'prometheus/1': {'workload-status': {'current': 'active',
258
 
                                                                                           'message': 'Ready',
259
 
                                                                                           'since': '16 Sep 2016 14:59:53Z'},
260
 
                                                                       'juju-status': {'current': 'idle',
261
 
                                                                                       'since': '16 Sep 2016 15:20:39Z',
262
 
                                                                                       'version': '2.0-beta18'},
263
 
                                                                       'machine': '2',
264
 
                                                                       'open-ports': ['9090/tcp'],
265
 
                                                                       'public-address': '10.47.34.203',
266
 
                                                                       'subordinates': {'telegraf/1': {
267
 
                                                                            'workload-status': {
268
 
                                                                                'current': 'active',
269
 
                                                                                'message': 'Monitoring prometheus/1',
270
 
                                                                                'since': '16 Sep 2016 15:23:55Z'},
271
 
                                                                            'juju-status': {'current': 'idle',
272
 
                                                                                            'since': '16 Sep 2016 15:23:55Z',
273
 
                                                                                            'version': '2.0-beta18'},
274
 
                                                                            'upgrading-from': 'cs:~telegraf-charmers/telegraf-6',
275
 
                                                                            'open-ports': ['9103/tcp'],
276
 
                                                                            'public-address': '10.47.34.203'}}}}},
277
 
                             'telegraf': {'charm': 'cs:~telegraf-charmers/telegraf-6',
278
 
                                          'series': 'xenial', 'os': 'ubuntu', 'charm-origin': 'jujucharms',
279
 
                                          'charm-name': 'telegraf', 'charm-rev': 6, 'exposed': False,
280
 
                                          'application-status': {'current': 'active',
281
 
                                                                 'message': 'Monitoring apache2/1',
282
 
                                                                 'since': '16 Sep 2016 15:23:55Z'},
283
 
                                          'relations': {'juju-info': ['apache2', 'prometheus']},
284
 
                                          'subordinate-to': ['apache2', 'prometheus']}}}
 
245
            "model": {
 
246
                "name": "mojo-test",
 
247
                "controller": "laptop",
 
248
                "cloud": "lxd",
 
249
                "region": "localhost",
 
250
                "version": "2.0-beta18",
 
251
            },
 
252
            "machines": {
 
253
                "2": {
 
254
                    "juju-status": {"current": "started", "since": "16 Sep 2016 14:56:46Z", "version": "2.0-beta18"},
 
255
                    "dns-name": "10.47.34.203",
 
256
                    "instance-id": "juju-fdef5d-2",
 
257
                    "machine-status": {"current": "running", "message": "Running", "since": "16 Sep 2016 14:55:58Z"},
 
258
                    "series": "xenial",
 
259
                    "hardware": "arch=amd64 cpu-cores=0 mem=0M",
 
260
                },
 
261
                "3": {
 
262
                    "juju-status": {"current": "started", "since": "16 Sep 2016 14:57:11Z", "version": "2.0-beta18"},
 
263
                    "dns-name": "fd28:938b:9119:802d:216:3eff:fe96:aa2d",
 
264
                    "instance-id": "juju-fdef5d-3",
 
265
                    "machine-status": {"current": "running", "message": "Running", "since": "16 Sep 2016 14:56:19Z"},
 
266
                    "series": "xenial",
 
267
                    "hardware": "arch=amd64 cpu-cores=0 mem=0M",
 
268
                },
 
269
            },
 
270
            "applications": {
 
271
                "apache2": {
 
272
                    "charm": "cs:trusty/apache2-20",
 
273
                    "series": "xenial",
 
274
                    "os": "ubuntu",
 
275
                    "charm-origin": "jujucharms",
 
276
                    "charm-name": "apache2",
 
277
                    "charm-rev": 20,
 
278
                    "exposed": False,
 
279
                    "application-status": {"current": "unknown", "since": "16 Sep 2016 14:57:53Z"},
 
280
                    "relations": {"juju-info": ["telegraf"]},
 
281
                    "units": {
 
282
                        "apache2/1": {
 
283
                            "workload-status": {"current": "unknown", "since": "16 Sep 2016 14:57:53Z"},
 
284
                            "juju-status": {
 
285
                                "current": "idle",
 
286
                                "since": "16 Sep 2016 15:20:34Z",
 
287
                                "version": "2.0-beta18",
 
288
                            },
 
289
                            "machine": "3",
 
290
                            "public-address": "fd28:938b:9119:802d:216:3eff:fe96:aa2d",
 
291
                            "subordinates": {
 
292
                                "telegraf/0": {
 
293
                                    "workload-status": {
 
294
                                        "current": "active",
 
295
                                        "message": "Monitoring apache2/1",
 
296
                                        "since": "16 Sep 2016 15:23:55Z",
 
297
                                    },
 
298
                                    "juju-status": {
 
299
                                        "current": "idle",
 
300
                                        "since": "16 Sep 2016 15:23:55Z",
 
301
                                        "version": "2.0-beta18",
 
302
                                    },
 
303
                                    "upgrading-from": "cs:~telegraf-charmers/telegraf-6",
 
304
                                    "open-ports": ["9103/tcp"],
 
305
                                    "public-address": "fd28:938b:9119:802d:216:3eff:fe96:aa2d",
 
306
                                }
 
307
                            },
 
308
                        }
 
309
                    },
 
310
                },
 
311
                "prometheus": {
 
312
                    "charm": "cs:~prometheus-charmers/prometheus-2",
 
313
                    "series": "xenial",
 
314
                    "os": "ubuntu",
 
315
                    "charm-origin": "jujucharms",
 
316
                    "charm-name": "prometheus",
 
317
                    "charm-rev": 2,
 
318
                    "exposed": False,
 
319
                    "application-status": {"current": "active", "message": "Ready", "since": "16 Sep 2016 14:59:53Z"},
 
320
                    "relations": {"juju-info": ["telegraf"]},
 
321
                    "units": {
 
322
                        "prometheus/1": {
 
323
                            "workload-status": {
 
324
                                "current": "active",
 
325
                                "message": "Ready",
 
326
                                "since": "16 Sep 2016 14:59:53Z",
 
327
                            },
 
328
                            "juju-status": {
 
329
                                "current": "idle",
 
330
                                "since": "16 Sep 2016 15:20:39Z",
 
331
                                "version": "2.0-beta18",
 
332
                            },
 
333
                            "machine": "2",
 
334
                            "open-ports": ["9090/tcp"],
 
335
                            "public-address": "10.47.34.203",
 
336
                            "subordinates": {
 
337
                                "telegraf/1": {
 
338
                                    "workload-status": {
 
339
                                        "current": "active",
 
340
                                        "message": "Monitoring prometheus/1",
 
341
                                        "since": "16 Sep 2016 15:23:55Z",
 
342
                                    },
 
343
                                    "juju-status": {
 
344
                                        "current": "idle",
 
345
                                        "since": "16 Sep 2016 15:23:55Z",
 
346
                                        "version": "2.0-beta18",
 
347
                                    },
 
348
                                    "upgrading-from": "cs:~telegraf-charmers/telegraf-6",
 
349
                                    "open-ports": ["9103/tcp"],
 
350
                                    "public-address": "10.47.34.203",
 
351
                                }
 
352
                            },
 
353
                        }
 
354
                    },
 
355
                },
 
356
                "telegraf": {
 
357
                    "charm": "cs:~telegraf-charmers/telegraf-6",
 
358
                    "series": "xenial",
 
359
                    "os": "ubuntu",
 
360
                    "charm-origin": "jujucharms",
 
361
                    "charm-name": "telegraf",
 
362
                    "charm-rev": 6,
 
363
                    "exposed": False,
 
364
                    "application-status": {
 
365
                        "current": "active",
 
366
                        "message": "Monitoring apache2/1",
 
367
                        "since": "16 Sep 2016 15:23:55Z",
 
368
                    },
 
369
                    "relations": {"juju-info": ["apache2", "prometheus"]},
 
370
                    "subordinate-to": ["apache2", "prometheus"],
 
371
                },
 
372
            },
 
373
        }
285
374
        self.assertEqual(self.status.yaml_status(force_update=True), expected)
286
375
 
287
376
 
300
389
        to use the KILL (9) signal, since this signal cannot be caught, in which
301
390
        case the exit status is 128+9 rather than 124."""
302
391
 
303
 
    @mock.patch('subprocess.check_output')
 
392
    @mock.patch("subprocess.check_output")
304
393
    def test_command_succeeds(self, check_output_mock):
305
394
        """command succeeds within timeout -> returns output, no exception"""
306
395
 
307
 
        command = ['/bin/echo', 'blorp']
308
 
        check_output_mock.return_value = 'blorp'
 
396
        command = ["/bin/echo", "blorp"]
 
397
        check_output_mock.return_value = "blorp"
309
398
 
310
399
        self.assertEqual(
311
400
            mojo.juju.status.check_output_with_timeout(
312
 
                command,
313
 
                command_timeout=5,
314
 
                timeout_exception=Juju2CheckOutputWithTimeoutTestException),
315
 
            'blorp')
 
401
                command, command_timeout=5, timeout_exception=Juju2CheckOutputWithTimeoutTestException
 
402
            ),
 
403
            "blorp",
 
404
        )
316
405
 
317
 
    @mock.patch('subprocess.check_output')
 
406
    @mock.patch("subprocess.check_output")
318
407
    def test_command_fails_within_timeout(self, check_output_mock):
319
408
        """command fails within timeout -> CalledProcessError thrown"""
320
409
 
321
410
        import subprocess
322
411
 
323
 
        command = ['/bin/echo', 'blorp']
 
412
        command = ["/bin/echo", "blorp"]
324
413
        check_output_mock.side_effect = subprocess.CalledProcessError(returncode=1, cmd=command)
325
414
 
326
415
        with self.assertRaises(subprocess.CalledProcessError) as arcm:
327
416
            mojo.juju.status.check_output_with_timeout(
328
 
                command,
329
 
                command_timeout=5,
330
 
                timeout_exception=Juju2CheckOutputWithTimeoutTestException)
 
417
                command, command_timeout=5, timeout_exception=Juju2CheckOutputWithTimeoutTestException
 
418
            )
331
419
 
332
420
        self.assertEqual(arcm.exception.returncode, 1)
333
421
 
334
 
    @mock.patch('subprocess.check_output')
 
422
    @mock.patch("subprocess.check_output")
335
423
    def test_command_times_out_and_is_terminated(self, check_output_mock):
336
424
        """command times out, SIGTERM exits -> CalledProcessError.returncode==124 -> timeout_exception('...')"""
337
425
 
338
426
        import subprocess
339
427
 
340
 
        command = ['/bin/echo', 'blorp']
 
428
        command = ["/bin/echo", "blorp"]
341
429
        check_output_mock.side_effect = subprocess.CalledProcessError(returncode=124, cmd=command)
342
430
 
343
431
        with self.assertRaises(Juju2CheckOutputWithTimeoutTestException) as arcm:
344
432
            mojo.juju.status.check_output_with_timeout(
345
 
                command,
346
 
                command_timeout=5,
347
 
                timeout_exception=Juju2CheckOutputWithTimeoutTestException)
348
 
 
349
 
        self.assertTrue(
350
 
            str(arcm.exception).endswith(' timed out after 5 seconds'))
351
 
 
352
 
    @mock.patch('subprocess.check_output')
 
433
                command, command_timeout=5, timeout_exception=Juju2CheckOutputWithTimeoutTestException
 
434
            )
 
435
 
 
436
        self.assertTrue(str(arcm.exception).endswith(" timed out after 5 seconds"))
 
437
 
 
438
    @mock.patch("subprocess.check_output")
353
439
    def test_command_times_out_and_is_killed(self, check_output_mock):
354
 
        """command times out, SIGKILL exits -> CalledProcessError.returncode==128+9 -> timeout_exception('... (killed)')"""
 
440
        """command times out
 
441
 
 
442
        SIGKILL exits -> CalledProcessError.returncode==128+9 -> timeout_exception('... (killed)')"""
355
443
 
356
444
        import subprocess
357
445
 
358
 
        command = ['/bin/echo', 'blorp']
359
 
        check_output_mock.side_effect = subprocess.CalledProcessError(returncode=128+9, cmd=command)
 
446
        command = ["/bin/echo", "blorp"]
 
447
        check_output_mock.side_effect = subprocess.CalledProcessError(returncode=128 + 9, cmd=command)
360
448
 
361
449
        with self.assertRaises(Juju2CheckOutputWithTimeoutTestException) as arcm:
362
450
            mojo.juju.status.check_output_with_timeout(
363
 
                command,
364
 
                command_timeout=5,
365
 
                timeout_exception=Juju2CheckOutputWithTimeoutTestException)
 
451
                command, command_timeout=5, timeout_exception=Juju2CheckOutputWithTimeoutTestException
 
452
            )
366
453
 
367
 
        self.assertTrue(
368
 
            str(arcm.exception).endswith(' timed out after 5 seconds (killed)'))
 
454
        self.assertTrue(str(arcm.exception).endswith(" timed out after 5 seconds (killed)"))
369
455
 
370
456
 
371
457
class Juju2ChecksTest(TestCase):
372
458
    def setUp(self):
373
 
        self.testdata = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'testdata')
374
 
        self.status_path = os.path.join(self.testdata, 'juju2-idle2.yaml')
 
459
        self.testdata = os.path.join(os.path.dirname(os.path.abspath(__file__)), "testdata")
 
460
        self.status_path = os.path.join(self.testdata, "juju2-idle2.yaml")
375
461
 
376
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
462
    @mock.patch("sys.stdout", new_callable=StringIO)
377
463
    def test_Errors_success(self, mock_stdout):
378
 
        sys.argv = ['self', '-f', self.status_path, 'errors']
 
464
        sys.argv = ["self", "-f", self.status_path, "errors"]
379
465
        return_code = check()
380
466
        self.assertEqual(0, return_code)
381
 
        self.assertEqual(mock_stdout.getvalue(), 'OK: No Juju status errors found.\n')
 
467
        self.assertEqual(mock_stdout.getvalue(), "OK: No Juju status errors found.\n")
382
468
 
383
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
469
    @mock.patch("sys.stdout", new_callable=StringIO)
384
470
    def test_Errors_failure(self, mock_stdout):
385
 
        error_status = os.path.join(self.testdata, 'juju2-fail.yaml')
386
 
        sys.argv = ['self', '-f', error_status, 'errors']
 
471
        error_status = os.path.join(self.testdata, "juju2-fail.yaml")
 
472
        sys.argv = ["self", "-f", error_status, "errors"]
387
473
        return_code = check()
388
474
        self.assertEqual(2, return_code)
389
 
        self.assertEqual(mock_stdout.getvalue(), 'CRITICAL: apache2 is in status error\n')
 
475
        self.assertEqual(mock_stdout.getvalue(), "CRITICAL: apache2 is in status error\n")
390
476
 
391
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
477
    @mock.patch("sys.stdout", new_callable=StringIO)
392
478
    def test_Unused_success(self, mock_stdout):
393
 
        sys.argv = ['self', '-f', self.status_path, 'unused_machines']
 
479
        sys.argv = ["self", "-f", self.status_path, "unused_machines"]
394
480
        return_code = check()
395
481
        self.assertEqual(0, return_code)
396
 
        self.assertEqual(mock_stdout.getvalue(), 'OK: All machines are being used.\n')
 
482
        self.assertEqual(mock_stdout.getvalue(), "OK: All machines are being used.\n")
397
483
 
398
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
484
    @mock.patch("sys.stdout", new_callable=StringIO)
399
485
    def test_Unused_failure(self, mock_stdout):
400
 
        unused_status = os.path.join(self.testdata, 'juju2-unused-machines.yaml')
401
 
        sys.argv = ['self', '-f', unused_status, 'unused_machines']
 
486
        unused_status = os.path.join(self.testdata, "juju2-unused-machines.yaml")
 
487
        sys.argv = ["self", "-f", unused_status, "unused_machines"]
402
488
        return_code = check()
403
489
        self.assertEqual(2, return_code)
404
490
        self.assertEqual(mock_stdout.getvalue(), "CRITICAL: Unused machines found: ['3']\n")
405
491
 
406
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
492
    @mock.patch("sys.stdout", new_callable=StringIO)
407
493
    def test_UpgradeAvailable_success(self, mock_stdout):
408
 
        sys.argv = ['self', '-f', self.status_path, 'upgrade_available', '--check_version', '2.0-rc1']
 
494
        sys.argv = ["self", "-f", self.status_path, "upgrade_available", "--check_version", "2.0-rc1"]
409
495
        return_code = check()
410
496
        self.assertEqual(0, return_code)
411
 
        self.assertEqual(mock_stdout.getvalue(), 'OK: The Juju controller and model are running the same version (2.0-rc1).\n')
 
497
        self.assertEqual(
 
498
            mock_stdout.getvalue(), "OK: The Juju controller and model are running the same version (2.0-rc1).\n"
 
499
        )
412
500
 
413
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
501
    @mock.patch("sys.stdout", new_callable=StringIO)
414
502
    def test_UpgradeAvailable_failure(self, mock_stdout):
415
 
        sys.argv = ['self', '-f', self.status_path, 'upgrade_available', '--check_version', '2.0-rc2']
 
503
        sys.argv = ["self", "-f", self.status_path, "upgrade_available", "--check_version", "2.0-rc2"]
416
504
        return_code = check()
417
505
        self.assertEqual(2, return_code)
418
 
        self.assertEqual(mock_stdout.getvalue(), 'CRITICAL: controller version 2.0-rc2 != model version 2.0-rc1\n')
 
506
        self.assertEqual(mock_stdout.getvalue(), "CRITICAL: controller version 2.0-rc2 != model version 2.0-rc1\n")
419
507
 
420
508
 
421
509
class Juju2ParseStatusTest(TestCase):
422
510
    def setUp(self):
423
 
        self.testdata = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'testdata')
424
 
        self.status_path = os.path.join(self.testdata, 'juju2-idle3.yaml')
 
511
        self.testdata = os.path.join(os.path.dirname(os.path.abspath(__file__)), "testdata")
 
512
        self.status_path = os.path.join(self.testdata, "juju2-idle3.yaml")
425
513
 
426
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
514
    @mock.patch("sys.stdout", new_callable=StringIO)
427
515
    def test_GetIPs_all(self, mock_stdout):
428
 
        sys.argv = ['self', '-f', self.status_path, 'get_ips', 'all']
429
 
        ips = list(sorted('10.25.2.111\n10.242.242.242\n10.25.2.109\n10.25.2.112\n10.25.2.110\n'.strip().split("\n")))
 
516
        sys.argv = ["self", "-f", self.status_path, "get_ips", "all"]
 
517
        ips = list(sorted("10.25.2.111\n10.242.242.242\n10.25.2.109\n10.25.2.112\n10.25.2.110\n".strip().split("\n")))
430
518
        rc = parse_status()
431
519
        self.assertEqual(rc, 0)
432
520
        self.assertEqual(list(sorted(mock_stdout.getvalue().strip().split("\n"))), ips)
433
521
 
434
 
    @mock.patch('sys.stdout', new_callable=StringIO)
 
522
    @mock.patch("sys.stdout", new_callable=StringIO)
435
523
    def test_GetIPs_app(self, mock_stdout):
436
 
        sys.argv = ['self', '-f', self.status_path, 'get_ips', 'ubuntu']
437
 
        ips = list(sorted('10.25.2.109\n10.25.2.111\n10.25.2.110\n'.strip().split("\n")))
 
524
        sys.argv = ["self", "-f", self.status_path, "get_ips", "ubuntu"]
 
525
        ips = list(sorted("10.25.2.109\n10.25.2.111\n10.25.2.110\n".strip().split("\n")))
438
526
        rc = parse_status()
439
527
        self.assertEqual(rc, 0)
440
528
        self.assertEqual(list(sorted(mock_stdout.getvalue().strip().split("\n"))), ips)