~andrewjbeach/juju-ci-tools/make-local-patcher

« back to all changes in this revision

Viewing changes to test_assess_recovery.py

  • Committer: Curtis Hovey
  • Date: 2015-06-11 19:35:22 UTC
  • mto: This revision was merged to the branch mainline in revision 983.
  • Revision ID: curtis@canonical.com-20150611193522-o2nqkqb04o2i75wv
Remove euca_dump_logs because it has not been used this year.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from contextlib import contextmanager
2
 
import logging
3
 
from mock import (
4
 
    call,
5
 
    patch,
6
 
    Mock,
7
 
    sentinel,
8
 
)
 
1
from unittest import TestCase
9
2
 
10
3
from assess_recovery import (
11
 
    assess_recovery,
12
 
    check_token,
13
 
    delete_controller_members,
14
 
    main,
15
4
    parse_args,
16
 
    restore_missing_state_server,
17
 
)
18
 
from jujupy import (
19
 
    Machine,
20
 
)
21
 
from tests import (
22
 
    FakeHomeTestCase,
23
 
    TestCase,
24
 
)
25
 
from tests.test_jujupy import fake_juju_client
26
 
from utility import JujuAssertionError
 
5
)
27
6
 
28
7
 
29
8
class TestParseArgs(TestCase):
30
9
 
31
10
    def test_parse_args(self):
32
 
        args = parse_args(['an-env', '/juju', 'log', 'tmp-env'])
33
 
        self.assertEqual(args.env, 'an-env')
34
 
        self.assertEqual(args.juju_bin, '/juju')
35
 
        self.assertEqual(args.logs, 'log')
36
 
        self.assertEqual(args.temp_env_name, 'tmp-env')
37
 
        self.assertEqual(args.charm_series, '')
 
11
        args = parse_args(['foo', 'bar', 'baz'])
 
12
        self.assertEqual(args.juju_path, 'foo')
 
13
        self.assertEqual(args.env_name, 'bar')
 
14
        self.assertEqual(args.logs, 'baz')
 
15
        self.assertEqual(args.charm_prefix, '')
38
16
        self.assertEqual(args.strategy, 'backup')
39
 
        self.assertEqual(args.verbose, logging.INFO)
40
17
        self.assertEqual(args.debug, False)
41
 
        self.assertIs(args.agent_stream, None)
42
 
        self.assertIs(args.series, None)
43
18
 
44
19
    def test_parse_args_ha(self):
45
 
        args = parse_args(['an-env', '/juju', 'log', 'tmp-env', '--ha'])
 
20
        args = parse_args(['foo', 'bar', 'baz', '--ha'])
46
21
        self.assertEqual(args.strategy, 'ha')
47
22
 
48
23
    def test_parse_args_ha_backup(self):
49
 
        args = parse_args(['an-env', '/juju', 'log', 'tmp-env', '--ha-backup'])
 
24
        args = parse_args(['foo', 'bar', 'baz', '--ha-backup'])
50
25
        self.assertEqual(args.strategy, 'ha-backup')
51
26
 
52
27
    def test_parse_args_backup(self):
53
 
        args = parse_args(['an-env', '/juju', 'log', 'tmp-env', '--ha',
54
 
                           '--backup'])
 
28
        args = parse_args(['foo', 'bar', 'baz', '--ha', '--backup'])
55
29
        self.assertEqual(args.strategy, 'backup')
56
30
 
57
 
    def test_parse_args_charm_series(self):
58
 
        args = parse_args(['an-env', '/juju', 'log', 'tmp-env',
59
 
                           '--charm-series', 'qux'])
60
 
        self.assertEqual(args.charm_series, 'qux')
61
 
 
62
 
 
63
 
class TestAssessRecovery(TestCase):
64
 
 
65
 
    @contextmanager
66
 
    def assess_recovery_cxt(self, client):
67
 
        client.bootstrap()
68
 
 
69
 
        def terminate(env, instance_ids):
70
 
            model = client._backend.controller_state.controller_model
71
 
            for instance_id in instance_ids:
72
 
                model.remove_state_server(instance_id)
73
 
 
74
 
        with patch('assess_recovery.wait_for_state_server_to_shutdown',
75
 
                   autospec=True):
76
 
            with patch('assess_recovery.terminate_instances',
77
 
                       side_effect=terminate):
78
 
                with patch('deploy_stack.wait_for_port', autospec=True):
79
 
                    with patch('assess_recovery.restore_present_state_server',
80
 
                               autospec=True):
81
 
                        with patch('assess_recovery.check_token',
82
 
                                   autospec=True,
83
 
                                   side_effect=['Token: One', 'Token: Two']):
84
 
                            with patch('assess_recovery.show_controller',
85
 
                                       autospec=True,
86
 
                                       return_value='controller'):
87
 
                                yield
88
 
 
89
 
    def test_backup(self):
90
 
        client = fake_juju_client()
91
 
        bs_manager = Mock(client=client, known_hosts={})
92
 
        with self.assess_recovery_cxt(client):
93
 
            assess_recovery(bs_manager, 'backup', 'trusty')
94
 
 
95
 
    def test_ha(self):
96
 
        client = fake_juju_client()
97
 
        bs_manager = Mock(client=client, known_hosts={})
98
 
        with self.assess_recovery_cxt(client):
99
 
            assess_recovery(bs_manager, 'ha', 'trusty')
100
 
 
101
 
    def test_ha_backup(self):
102
 
        client = fake_juju_client()
103
 
        bs_manager = Mock(client=client, known_hosts={})
104
 
        with self.assess_recovery_cxt(client):
105
 
            assess_recovery(bs_manager, 'ha-backup', 'trusty')
106
 
 
107
 
    def test_controller_model_backup(self):
108
 
        client = fake_juju_client()
109
 
        bs_manager = Mock(client=client, known_hosts={})
110
 
        with self.assess_recovery_cxt(client):
111
 
            assess_recovery(bs_manager, 'backup', 'trusty')
112
 
 
113
 
    def test_controller_model_ha(self):
114
 
        client = fake_juju_client()
115
 
        bs_manager = Mock(client=client, known_hosts={})
116
 
        with self.assess_recovery_cxt(client):
117
 
            assess_recovery(bs_manager, 'ha', 'trusty')
118
 
 
119
 
    def test_controller_model_ha_backup(self):
120
 
        client = fake_juju_client()
121
 
        bs_manager = Mock(client=client, known_hosts={})
122
 
        with self.assess_recovery_cxt(client):
123
 
            assess_recovery(bs_manager, 'ha-backup', 'trusty')
124
 
 
125
 
 
126
 
@patch('assess_recovery.configure_logging', autospec=True)
127
 
@patch('assess_recovery.BootstrapManager.booted_context', autospec=True)
128
 
class TestMain(FakeHomeTestCase):
129
 
 
130
 
    def test_main(self, mock_bc, mock_cl):
131
 
        client = Mock(spec=['is_jes_enabled', 'version'])
132
 
        client.version = '1.25.5'
133
 
        with patch('deploy_stack.client_from_config',
134
 
                   return_value=client) as mock_c:
135
 
            with patch('assess_recovery.assess_recovery',
136
 
                       autospec=True) as mock_assess:
137
 
                main(['an-env', '/juju', 'log_dir', 'tmp-env', '--backup',
138
 
                      '--charm-series', 'a-series'])
139
 
        mock_cl.assert_called_once_with(logging.INFO)
140
 
        mock_c.assert_called_once_with('an-env', '/juju', debug=False,
141
 
                                       soft_deadline=None)
142
 
        self.assertEqual(mock_bc.call_count, 1)
143
 
        self.assertEqual(mock_assess.call_count, 1)
144
 
        bs_manager, strategy, series = mock_assess.call_args[0]
145
 
        self.assertEqual((bs_manager.client, strategy, series),
146
 
                         (client, 'backup', 'a-series'))
147
 
 
148
 
    def test_error(self, mock_bc, mock_cl):
149
 
        class FakeError(Exception):
150
 
            """Custom exception to validate error handling."""
151
 
        error = FakeError('An error during test')
152
 
        client = Mock(spec=['is_jes_enabled', 'version'])
153
 
        client.version = '2.0.0'
154
 
        with patch('deploy_stack.client_from_config',
155
 
                   return_value=client) as mock_c:
156
 
            with patch('assess_recovery.parse_new_state_server_from_error',
157
 
                       autospec=True, return_value='a-host') as mock_pe:
158
 
                with patch('assess_recovery.assess_recovery', autospec=True,
159
 
                           side_effect=error) as mock_assess:
160
 
                    with self.assertRaises(FakeError) as ctx:
161
 
                        main(['an-env', '/juju', 'log_dir', 'tmp-env', '--ha',
162
 
                              '--verbose', '--charm-series', 'a-series'])
163
 
                    self.assertIs(ctx.exception, error)
164
 
        mock_cl.assert_called_once_with(logging.DEBUG)
165
 
        mock_c.assert_called_once_with('an-env', '/juju', debug=False,
166
 
                                       soft_deadline=None)
167
 
        mock_pe.assert_called_once_with(error)
168
 
        self.assertEqual(mock_bc.call_count, 1)
169
 
        self.assertEqual(mock_assess.call_count, 1)
170
 
        bs_manager, strategy, series = mock_assess.call_args[0]
171
 
        self.assertEqual((bs_manager.client, strategy, series),
172
 
                         (client, 'ha', 'a-series'))
173
 
        self.assertEqual(bs_manager.known_hosts['0'], 'a-host')
174
 
 
175
 
 
176
 
@patch('assess_recovery.wait_for_state_server_to_shutdown', autospec=True)
177
 
@patch('assess_recovery.terminate_instances', autospec=True)
178
 
class TestDeleteControllerMembers(FakeHomeTestCase):
179
 
 
180
 
    def test_delete_controller_members(self, ti_mock, wsss_mock):
181
 
        client = Mock(spec=['env', 'get_controller_members'])
182
 
        client.env = sentinel.env
183
 
        client.env.config = {'type': 'lxd'}
184
 
        client.get_controller_members.return_value = [
185
 
            Machine('3', {
186
 
                'dns-name': '10.0.0.3',
187
 
                'instance-id': 'juju-dddd-machine-3',
188
 
                'controller-member-status': 'has-vote'}),
189
 
            Machine('0', {
190
 
                'dns-name': '10.0.0.0',
191
 
                'instance-id': 'juju-aaaa-machine-0',
192
 
                'controller-member-status': 'has-vote'}),
193
 
            Machine('2', {
194
 
                'dns-name': '10.0.0.2',
195
 
                'instance-id': 'juju-cccc-machine-2',
196
 
                'controller-member-status': 'has-vote'}),
197
 
        ]
198
 
        deleted = delete_controller_members(client)
199
 
        self.assertEqual(['2', '0', '3'], deleted)
200
 
        client.get_controller_members.assert_called_once_with()
201
 
        # terminate_instance was call in the reverse order of members.
202
 
        self.assertEqual(
203
 
            [call(client.env, ['juju-cccc-machine-2']),
204
 
             call(client.env, ['juju-aaaa-machine-0']),
205
 
             call(client.env, ['juju-dddd-machine-3'])],
206
 
            ti_mock.mock_calls)
207
 
        self.assertEqual(
208
 
            [call('10.0.0.2', client, 'juju-cccc-machine-2', timeout=120),
209
 
             call('10.0.0.0', client, 'juju-aaaa-machine-0', timeout=120),
210
 
             call('10.0.0.3', client, 'juju-dddd-machine-3', timeout=120)],
211
 
            wsss_mock.mock_calls)
212
 
        self.assertEqual(
213
 
            self.log_stream.getvalue(),
214
 
            'INFO Instrumenting node failure for member 2:'
215
 
            ' juju-cccc-machine-2 at 10.0.0.2\n'
216
 
            'INFO Instrumenting node failure for member 0:'
217
 
            ' juju-aaaa-machine-0 at 10.0.0.0\n'
218
 
            'INFO Instrumenting node failure for member 3:'
219
 
            ' juju-dddd-machine-3 at 10.0.0.3\n')
220
 
 
221
 
    def test_delete_controller_members_leader_only(self, ti_mock, wsss_mock):
222
 
        client = Mock(spec=['env', 'get_controller_leader'])
223
 
        client.env = sentinel.env
224
 
        client.env.config = {'type': 'lxd'}
225
 
        client.get_controller_leader.return_value = Machine('3', {
226
 
            'dns-name': '10.0.0.3',
227
 
            'instance-id': 'juju-dddd-machine-3',
228
 
            'controller-member-status': 'has-vote'})
229
 
        deleted = delete_controller_members(client, leader_only=True)
230
 
        self.assertEqual(['3'], deleted)
231
 
        client.get_controller_leader.assert_called_once_with()
232
 
        ti_mock.assert_called_once_with(client.env, ['juju-dddd-machine-3'])
233
 
        wsss_mock.assert_called_once_with(
234
 
            '10.0.0.3', client, 'juju-dddd-machine-3', timeout=120)
235
 
        self.assertEqual(
236
 
            self.log_stream.getvalue(),
237
 
            'INFO Instrumenting node failure for member 3:'
238
 
            ' juju-dddd-machine-3 at 10.0.0.3\n')
239
 
 
240
 
    def test_delete_controller_members_azure(self, ti_mock, wsss_mock):
241
 
        client = Mock(spec=['env', 'get_controller_leader'])
242
 
        client.env = sentinel.env
243
 
        client.env.config = {'type': 'azure'}
244
 
        client.get_controller_leader.return_value = Machine('3', {
245
 
            'dns-name': '10.0.0.3',
246
 
            'instance-id': 'juju-dddd-machine-3',
247
 
            'controller-member-status': 'has-vote'})
248
 
        with patch('assess_recovery.convert_to_azure_ids', autospec=True,
249
 
                   return_value=['juju-azure-id']):
250
 
            deleted = delete_controller_members(client, leader_only=True)
251
 
        self.assertEqual(['3'], deleted)
252
 
        client.get_controller_leader.assert_called_once_with()
253
 
        ti_mock.assert_called_once_with(client.env, ['juju-azure-id'])
254
 
        wsss_mock.assert_called_once_with(
255
 
            '10.0.0.3', client, 'juju-azure-id', timeout=120)
256
 
        self.assertEqual(
257
 
            self.log_stream.getvalue(),
258
 
            'INFO Instrumenting node failure for member 3:'
259
 
            ' juju-azure-id at 10.0.0.3\n')
260
 
 
261
 
 
262
 
class TestRestoreMissingStateServer(FakeHomeTestCase):
263
 
 
264
 
    def test_restore_missing_state_server_with_check_controller(self):
265
 
        client = Mock(spec=['env', 'set_config', 'wait_for_started',
266
 
                            'wait_for_workloads'])
267
 
        controller_client = Mock(spec=['restore_backup', 'wait_for_started'])
268
 
        with patch('assess_recovery.check_token',
269
 
                   autospec=True, return_value='Token: Two'):
270
 
            with patch('assess_recovery.show_controller', autospec=True):
271
 
                restore_missing_state_server(
272
 
                    client, controller_client, 'backup_file',
273
 
                    check_controller=True)
274
 
        controller_client.restore_backup.assert_called_once_with('backup_file')
275
 
        controller_client.wait_for_started.assert_called_once_with(600)
276
 
        client.set_config.assert_called_once_with(
277
 
            'dummy-source', {'token': 'Two'})
278
 
        client.wait_for_started.assert_called_once_with()
279
 
        client.wait_for_workloads.assert_called_once_with()
280
 
 
281
 
    def test_restore_missing_state_server_without_check_controller(self):
282
 
        client = Mock(spec=['env', 'set_config', 'wait_for_started',
283
 
                            'wait_for_workloads'])
284
 
        controller_client = Mock(spec=['restore_backup', 'wait_for_started'])
285
 
        with patch('assess_recovery.check_token',
286
 
                   autospec=True, return_value='Token: Two'):
287
 
            with patch('assess_recovery.show_controller', autospec=True):
288
 
                restore_missing_state_server(
289
 
                    client, controller_client, 'backup_file',
290
 
                    check_controller=False)
291
 
        self.assertEqual(0, controller_client.wait_for_started.call_count)
292
 
 
293
 
 
294
 
class TestCheckToken(TestCase):
295
 
 
296
 
    def test_check_token_found(self):
297
 
        client = Mock()
298
 
        with patch('assess_recovery.get_token_from_status', autospec=True,
299
 
                   side_effect=['Token: foo']):
300
 
            found = check_token(client, 'foo')
301
 
        self.assertEqual('Token: foo', found)
302
 
 
303
 
    def test_check_token_none_before_found(self):
304
 
        client = Mock()
305
 
        with patch('assess_recovery.get_token_from_status', autospec=True,
306
 
                   side_effect=[None, 'foo']):
307
 
            found = check_token(client, 'foo')
308
 
        self.assertEqual('foo', found)
309
 
 
310
 
    def test_check_token_other_before_found(self):
311
 
        client = Mock()
312
 
        with patch('assess_recovery.get_token_from_status', autospec=True,
313
 
                   side_effect=['Starting', 'foo']):
314
 
            found = check_token(client, 'foo')
315
 
        self.assertEqual('foo', found)
316
 
 
317
 
    def test_check_token_not_found(self):
318
 
        client = Mock()
319
 
        with patch('assess_recovery.get_token_from_status', autospec=True,
320
 
                   return_value='other'):
321
 
            with patch('assess_recovery.until_timeout', autospec=True,
322
 
                       side_effect=['1', '0']):
323
 
                with self.assertRaises(JujuAssertionError):
324
 
                    check_token(client, 'foo')
 
31
    def test_parse_args_charm_prefix(self):
 
32
        args = parse_args(['foo', 'bar', 'baz', '--charm-prefix', 'qux'])
 
33
        self.assertEqual(args.charm_prefix, 'qux')
 
34
 
 
35
    def test_parse_args_debug(self):
 
36
        args = parse_args(['foo', 'bar', 'baz', '--debug'])
 
37
        self.assertEqual(args.debug, True)
 
38
 
 
39
    def test_parse_args_temp_env_name(self):
 
40
        args = parse_args(['foo', 'bar', 'baz'])
 
41
        self.assertIs(args.temp_env_name, None)
 
42
        args = parse_args(['foo', 'bar', 'baz', 'qux'])
 
43
        self.assertEqual(args.temp_env_name, 'qux')