~gnuoy/charms/trusty/keystone/bug1403132

« back to all changes in this revision

Viewing changes to unit_tests/test_keystone_hooks.py

[hopem,r=gnuoy]
 
Fixes ssl cert synchronisation across peers

Closes-Bug: 1317782

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
from mock import call, patch, MagicMock
2
2
import os
3
3
import json
 
4
import uuid
4
5
 
5
6
from test_utils import CharmTestCase
6
7
 
30
31
    'local_unit',
31
32
    'filter_installed_packages',
32
33
    'relation_ids',
33
 
    'relation_list',
34
34
    'relation_set',
35
35
    'relation_get',
36
36
    'related_units',
42
42
    'restart_on_change',
43
43
    # charmhelpers.contrib.openstack.utils
44
44
    'configure_installation_source',
 
45
    # charmhelpers.contrib.openstack.ip
 
46
    'resolve_address',
45
47
    # charmhelpers.contrib.hahelpers.cluster_utils
46
 
    'is_leader',
47
 
    'eligible_leader',
 
48
    'is_elected_leader',
48
49
    'get_hacluster_config',
49
50
    # keystone_utils
50
51
    'restart_map',
55
56
    'migrate_database',
56
57
    'ensure_initial_admin',
57
58
    'add_service_to_keystone',
58
 
    'synchronize_ca',
 
59
    'synchronize_ca_if_changed',
59
60
    'update_nrpe_config',
60
61
    # other
61
62
    'check_call',
160
161
            'Attempting to associate a postgresql database when there '
161
162
            'is already associated a mysql one')
162
163
 
 
164
    @patch('keystone_utils.log')
 
165
    @patch('keystone_utils.ensure_ssl_cert_master')
163
166
    @patch.object(hooks, 'CONFIGS')
164
 
    def test_db_changed_missing_relation_data(self, configs):
 
167
    def test_db_changed_missing_relation_data(self, configs,
 
168
                                              mock_ensure_ssl_cert_master,
 
169
                                              mock_log):
 
170
        mock_ensure_ssl_cert_master.return_value = False
165
171
        configs.complete_contexts = MagicMock()
166
172
        configs.complete_contexts.return_value = []
167
173
        hooks.db_changed()
169
175
            'shared-db relation incomplete. Peer not ready?'
170
176
        )
171
177
 
 
178
    @patch('keystone_utils.log')
 
179
    @patch('keystone_utils.ensure_ssl_cert_master')
172
180
    @patch.object(hooks, 'CONFIGS')
173
 
    def test_postgresql_db_changed_missing_relation_data(self, configs):
 
181
    def test_postgresql_db_changed_missing_relation_data(self, configs,
 
182
                                                         mock_ensure_leader,
 
183
                                                         mock_log):
 
184
        mock_ensure_leader.return_value = False
174
185
        configs.complete_contexts = MagicMock()
175
186
        configs.complete_contexts.return_value = []
176
187
        hooks.pgsql_db_changed()
192
203
        configs.write = MagicMock()
193
204
        hooks.pgsql_db_changed()
194
205
 
 
206
    @patch('keystone_utils.log')
 
207
    @patch('keystone_utils.ensure_ssl_cert_master')
195
208
    @patch.object(hooks, 'CONFIGS')
196
209
    @patch.object(hooks, 'identity_changed')
197
 
    def test_db_changed_allowed(self, identity_changed, configs):
 
210
    def test_db_changed_allowed(self, identity_changed, configs,
 
211
                                mock_ensure_ssl_cert_master,
 
212
                                mock_log):
 
213
        mock_ensure_ssl_cert_master.return_value = False
198
214
        self.relation_ids.return_value = ['identity-service:0']
199
215
        self.related_units.return_value = ['unit/0']
200
216
 
207
223
            relation_id='identity-service:0',
208
224
            remote_unit='unit/0')
209
225
 
 
226
    @patch('keystone_utils.log')
 
227
    @patch('keystone_utils.ensure_ssl_cert_master')
210
228
    @patch.object(hooks, 'CONFIGS')
211
229
    @patch.object(hooks, 'identity_changed')
212
 
    def test_db_changed_not_allowed(self, identity_changed, configs):
 
230
    def test_db_changed_not_allowed(self, identity_changed, configs,
 
231
                                    mock_ensure_ssl_cert_master, mock_log):
 
232
        mock_ensure_ssl_cert_master.return_value = False
213
233
        self.relation_ids.return_value = ['identity-service:0']
214
234
        self.related_units.return_value = ['unit/0']
215
235
 
220
240
        self.assertFalse(self.ensure_initial_admin.called)
221
241
        self.assertFalse(identity_changed.called)
222
242
 
 
243
    @patch('keystone_utils.log')
 
244
    @patch('keystone_utils.ensure_ssl_cert_master')
223
245
    @patch.object(hooks, 'CONFIGS')
224
246
    @patch.object(hooks, 'identity_changed')
225
 
    def test_postgresql_db_changed(self, identity_changed, configs):
 
247
    def test_postgresql_db_changed(self, identity_changed, configs,
 
248
                                   mock_ensure_ssl_cert_master, mock_log):
 
249
        mock_ensure_ssl_cert_master.return_value = False
226
250
        self.relation_ids.return_value = ['identity-service:0']
227
251
        self.related_units.return_value = ['unit/0']
228
252
 
235
259
            relation_id='identity-service:0',
236
260
            remote_unit='unit/0')
237
261
 
 
262
    @patch('keystone_utils.log')
 
263
    @patch('keystone_utils.ensure_ssl_cert_master')
 
264
    @patch.object(hooks, 'peer_units')
 
265
    @patch.object(hooks, 'ensure_permissions')
238
266
    @patch.object(hooks, 'admin_relation_changed')
239
267
    @patch.object(hooks, 'cluster_joined')
240
268
    @patch.object(unison, 'ensure_user')
245
273
    def test_config_changed_no_openstack_upgrade_leader(
246
274
            self, configure_https, identity_changed,
247
275
            configs, get_homedir, ensure_user, cluster_joined,
248
 
            admin_relation_changed):
 
276
            admin_relation_changed, ensure_permissions, mock_peer_units,
 
277
            mock_ensure_ssl_cert_master, mock_log):
249
278
        self.openstack_upgrade_available.return_value = False
250
 
        self.eligible_leader.return_value = True
251
 
        self.relation_ids.return_value = ['dummyid:0']
252
 
        self.relation_list.return_value = ['unit/0']
 
279
        self.is_elected_leader.return_value = True
 
280
        # avoid having to mock syncer
 
281
        mock_ensure_ssl_cert_master.return_value = False
 
282
        mock_peer_units.return_value = []
 
283
        self.relation_ids.return_value = ['identity-service:0']
 
284
        self.related_units.return_value = ['unit/0']
253
285
 
254
286
        hooks.config_changed()
255
287
        ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
264
296
        self.log.assert_called_with(
265
297
            'Firing identity_changed hook for all related services.')
266
298
        identity_changed.assert_called_with(
267
 
            relation_id='dummyid:0',
 
299
            relation_id='identity-service:0',
268
300
            remote_unit='unit/0')
269
 
        admin_relation_changed.assert_called_with('dummyid:0')
 
301
        admin_relation_changed.assert_called_with('identity-service:0')
270
302
 
 
303
    @patch('keystone_utils.log')
 
304
    @patch('keystone_utils.ensure_ssl_cert_master')
 
305
    @patch.object(hooks, 'ensure_permissions')
271
306
    @patch.object(hooks, 'cluster_joined')
272
307
    @patch.object(unison, 'ensure_user')
273
308
    @patch.object(unison, 'get_homedir')
276
311
    @patch.object(hooks, 'configure_https')
277
312
    def test_config_changed_no_openstack_upgrade_not_leader(
278
313
            self, configure_https, identity_changed,
279
 
            configs, get_homedir, ensure_user, cluster_joined):
 
314
            configs, get_homedir, ensure_user, cluster_joined,
 
315
            ensure_permissions, mock_ensure_ssl_cert_master,
 
316
            mock_log):
280
317
        self.openstack_upgrade_available.return_value = False
281
 
        self.eligible_leader.return_value = False
 
318
        self.is_elected_leader.return_value = False
 
319
        mock_ensure_ssl_cert_master.return_value = False
282
320
 
283
321
        hooks.config_changed()
284
322
        ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
292
330
        self.assertFalse(self.ensure_initial_admin.called)
293
331
        self.assertFalse(identity_changed.called)
294
332
 
 
333
    @patch('keystone_utils.log')
 
334
    @patch('keystone_utils.ensure_ssl_cert_master')
 
335
    @patch.object(hooks, 'peer_units')
 
336
    @patch.object(hooks, 'ensure_permissions')
295
337
    @patch.object(hooks, 'admin_relation_changed')
296
338
    @patch.object(hooks, 'cluster_joined')
297
339
    @patch.object(unison, 'ensure_user')
302
344
    def test_config_changed_with_openstack_upgrade(
303
345
            self, configure_https, identity_changed,
304
346
            configs, get_homedir, ensure_user, cluster_joined,
305
 
            admin_relation_changed):
 
347
            admin_relation_changed,
 
348
            ensure_permissions, mock_peer_units, mock_ensure_ssl_cert_master,
 
349
            mock_log):
306
350
        self.openstack_upgrade_available.return_value = True
307
 
        self.eligible_leader.return_value = True
308
 
        self.relation_ids.return_value = ['dummyid:0']
309
 
        self.relation_list.return_value = ['unit/0']
 
351
        self.is_elected_leader.return_value = True
 
352
        # avoid having to mock syncer
 
353
        mock_ensure_ssl_cert_master.return_value = False
 
354
        mock_peer_units.return_value = []
 
355
        self.relation_ids.return_value = ['identity-service:0']
 
356
        self.related_units.return_value = ['unit/0']
310
357
 
311
358
        hooks.config_changed()
312
359
        ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
323
370
        self.log.assert_called_with(
324
371
            'Firing identity_changed hook for all related services.')
325
372
        identity_changed.assert_called_with(
326
 
            relation_id='dummyid:0',
 
373
            relation_id='identity-service:0',
327
374
            remote_unit='unit/0')
328
 
        admin_relation_changed.assert_called_with('dummyid:0')
 
375
        admin_relation_changed.assert_called_with('identity-service:0')
329
376
 
 
377
    @patch('keystone_utils.log')
 
378
    @patch('keystone_utils.ensure_ssl_cert_master')
330
379
    @patch.object(hooks, 'hashlib')
331
380
    @patch.object(hooks, 'send_notifications')
332
381
    def test_identity_changed_leader(self, mock_send_notifications,
333
 
                                     mock_hashlib):
334
 
        self.eligible_leader.return_value = True
 
382
                                     mock_hashlib, mock_ensure_ssl_cert_master,
 
383
                                     mock_log):
 
384
        mock_ensure_ssl_cert_master.return_value = False
335
385
        hooks.identity_changed(
336
386
            relation_id='identity-service:0',
337
387
            remote_unit='unit/0')
338
388
        self.add_service_to_keystone.assert_called_with(
339
389
            'identity-service:0',
340
390
            'unit/0')
341
 
        self.assertTrue(self.synchronize_ca.called)
342
391
 
343
 
    def test_identity_changed_no_leader(self):
344
 
        self.eligible_leader.return_value = False
 
392
    @patch.object(hooks, 'local_unit')
 
393
    @patch('keystone_utils.log')
 
394
    @patch('keystone_utils.ensure_ssl_cert_master')
 
395
    def test_identity_changed_no_leader(self, mock_ensure_ssl_cert_master,
 
396
                                        mock_log, mock_local_unit):
 
397
        mock_ensure_ssl_cert_master.return_value = False
 
398
        mock_local_unit.return_value = 'unit/0'
 
399
        self.is_elected_leader.return_value = False
345
400
        hooks.identity_changed(
346
401
            relation_id='identity-service:0',
347
402
            remote_unit='unit/0')
349
404
        self.log.assert_called_with(
350
405
            'Deferring identity_changed() to service leader.')
351
406
 
 
407
    @patch.object(hooks, 'local_unit')
 
408
    @patch.object(hooks, 'peer_units')
352
409
    @patch.object(unison, 'ssh_authorized_peers')
353
 
    def test_cluster_joined(self, ssh_authorized_peers):
 
410
    def test_cluster_joined(self, ssh_authorized_peers, mock_peer_units,
 
411
                            mock_local_unit):
 
412
        mock_local_unit.return_value = 'unit/0'
 
413
        mock_peer_units.return_value = ['unit/0']
354
414
        hooks.cluster_joined()
355
415
        ssh_authorized_peers.assert_called_with(
356
416
            user=self.ssh_user, group='juju_keystone',
357
417
            peer_interface='cluster', ensure_local_user=True)
358
418
 
 
419
    @patch.object(hooks, 'is_ssl_cert_master')
 
420
    @patch.object(hooks, 'peer_units')
 
421
    @patch('keystone_utils.log')
 
422
    @patch('keystone_utils.ensure_ssl_cert_master')
 
423
    @patch('keystone_utils.synchronize_ca')
 
424
    @patch.object(hooks, 'check_peer_actions')
359
425
    @patch.object(unison, 'ssh_authorized_peers')
360
426
    @patch.object(hooks, 'CONFIGS')
361
 
    def test_cluster_changed(self, configs, ssh_authorized_peers):
 
427
    def test_cluster_changed(self, configs, ssh_authorized_peers,
 
428
                             check_peer_actions, mock_synchronize_ca,
 
429
                             mock_ensure_ssl_cert_master,
 
430
                             mock_log, mock_peer_units,
 
431
                             mock_is_ssl_cert_master):
 
432
        mock_is_ssl_cert_master.return_value = False
 
433
        mock_peer_units.return_value = ['unit/0']
 
434
        mock_ensure_ssl_cert_master.return_value = False
 
435
        self.is_elected_leader.return_value = False
 
436
        self.relation_get.return_value = {'foo_passwd': '123',
 
437
                                          'identity-service:16_foo': 'bar'}
362
438
        hooks.cluster_changed()
363
 
        self.peer_echo.assert_called_with(includes=['_passwd',
364
 
                                          'identity-service:'])
 
439
        self.peer_echo.assert_called_with(includes=['foo_passwd',
 
440
                                                    'identity-service:16_foo'])
365
441
        ssh_authorized_peers.assert_called_with(
366
442
            user=self.ssh_user, group='keystone',
367
443
            peer_interface='cluster', ensure_local_user=True)
368
 
        self.assertTrue(self.synchronize_ca.called)
 
444
        self.assertFalse(mock_synchronize_ca.called)
369
445
        self.assertTrue(configs.write_all.called)
370
446
 
371
447
    def test_ha_joined(self):
440
516
        }
441
517
        self.relation_set.assert_called_with(**args)
442
518
 
 
519
    @patch('keystone_utils.log')
 
520
    @patch('keystone_utils.ensure_ssl_cert_master')
 
521
    @patch('keystone_utils.synchronize_ca')
443
522
    @patch.object(hooks, 'CONFIGS')
444
 
    def test_ha_relation_changed_not_clustered_not_leader(self, configs):
 
523
    def test_ha_relation_changed_not_clustered_not_leader(self, configs,
 
524
                                                          mock_synchronize_ca,
 
525
                                                          mock_is_master,
 
526
                                                          mock_log):
 
527
        mock_is_master.return_value = False
445
528
        self.relation_get.return_value = False
446
 
        self.is_leader.return_value = False
 
529
        self.is_elected_leader.return_value = False
447
530
 
448
531
        hooks.ha_changed()
449
532
        self.assertTrue(configs.write_all.called)
 
533
        self.assertFalse(mock_synchronize_ca.called)
450
534
 
 
535
    @patch('keystone_utils.log')
 
536
    @patch('keystone_utils.ensure_ssl_cert_master')
451
537
    @patch.object(hooks, 'identity_changed')
452
538
    @patch.object(hooks, 'CONFIGS')
453
 
    def test_ha_relation_changed_clustered_leader(
454
 
            self, configs, identity_changed):
 
539
    def test_ha_relation_changed_clustered_leader(self, configs,
 
540
                                                  identity_changed,
 
541
                                                  mock_ensure_ssl_cert_master,
 
542
                                                  mock_log):
 
543
        mock_ensure_ssl_cert_master.return_value = False
455
544
        self.relation_get.return_value = True
456
 
        self.is_leader.return_value = True
 
545
        self.is_elected_leader.return_value = True
457
546
        self.relation_ids.return_value = ['identity-service:0']
458
547
        self.related_units.return_value = ['unit/0']
459
548
 
460
549
        hooks.ha_changed()
461
550
        self.assertTrue(configs.write_all.called)
462
551
        self.log.assert_called_with(
463
 
            'Cluster configured, notifying other services and updating '
464
 
            'keystone endpoint configuration')
 
552
            'Firing identity_changed hook for all related services.')
465
553
        identity_changed.assert_called_with(
466
554
            relation_id='identity-service:0',
467
555
            remote_unit='unit/0')
468
556
 
 
557
    @patch('keystone_utils.log')
 
558
    @patch('keystone_utils.ensure_ssl_cert_master')
469
559
    @patch.object(hooks, 'CONFIGS')
470
 
    def test_configure_https_enable(self, configs):
 
560
    def test_configure_https_enable(self, configs, mock_ensure_ssl_cert_master,
 
561
                                    mock_log):
 
562
        mock_ensure_ssl_cert_master.return_value = False
471
563
        configs.complete_contexts = MagicMock()
472
564
        configs.complete_contexts.return_value = ['https']
473
565
        configs.write = MagicMock()
477
569
        cmd = ['a2ensite', 'openstack_https_frontend']
478
570
        self.check_call.assert_called_with(cmd)
479
571
 
 
572
    @patch('keystone_utils.log')
 
573
    @patch('keystone_utils.ensure_ssl_cert_master')
480
574
    @patch.object(hooks, 'CONFIGS')
481
 
    def test_configure_https_disable(self, configs):
 
575
    def test_configure_https_disable(self, configs,
 
576
                                     mock_ensure_ssl_cert_master,
 
577
                                     mock_log):
 
578
        mock_ensure_ssl_cert_master.return_value = False
482
579
        configs.complete_contexts = MagicMock()
483
580
        configs.complete_contexts.return_value = ['']
484
581
        configs.write = MagicMock()
488
585
        cmd = ['a2dissite', 'openstack_https_frontend']
489
586
        self.check_call.assert_called_with(cmd)
490
587
 
 
588
    @patch('keystone_utils.log')
 
589
    @patch('keystone_utils.relation_ids')
 
590
    @patch('keystone_utils.is_elected_leader')
 
591
    @patch('keystone_utils.ensure_ssl_cert_master')
 
592
    @patch('keystone_utils.update_hash_from_path')
 
593
    @patch('keystone_utils.synchronize_ca')
491
594
    @patch.object(unison, 'ssh_authorized_peers')
492
 
    def test_upgrade_charm_leader(self, ssh_authorized_peers):
493
 
        self.eligible_leader.return_value = True
 
595
    def test_upgrade_charm_leader(self, ssh_authorized_peers,
 
596
                                  mock_synchronize_ca,
 
597
                                  mock_update_hash_from_path,
 
598
                                  mock_ensure_ssl_cert_master,
 
599
                                  mock_is_elected_leader,
 
600
                                  mock_relation_ids,
 
601
                                  mock_log):
 
602
        mock_is_elected_leader.return_value = False
 
603
        mock_relation_ids.return_value = []
 
604
        mock_ensure_ssl_cert_master.return_value = True
 
605
        # Ensure always returns diff
 
606
        mock_update_hash_from_path.side_effect = \
 
607
            lambda hash, *args, **kwargs: hash.update(str(uuid.uuid4()))
 
608
 
 
609
        self.is_elected_leader.return_value = True
494
610
        self.filter_installed_packages.return_value = []
495
611
        hooks.upgrade_charm()
496
612
        self.assertTrue(self.apt_install.called)
497
613
        ssh_authorized_peers.assert_called_with(
498
614
            user=self.ssh_user, group='keystone',
499
615
            peer_interface='cluster', ensure_local_user=True)
500
 
        self.assertTrue(self.synchronize_ca.called)
 
616
        self.assertTrue(mock_synchronize_ca.called)
501
617
        self.log.assert_called_with(
502
 
            'Cluster leader - ensuring endpoint configuration'
503
 
            ' is up to date')
 
618
            'Firing identity_changed hook for all related services.')
504
619
        self.assertTrue(self.ensure_initial_admin.called)
505
620
 
 
621
    @patch('keystone_utils.log')
 
622
    @patch('keystone_utils.relation_ids')
 
623
    @patch('keystone_utils.ensure_ssl_cert_master')
 
624
    @patch('keystone_utils.update_hash_from_path')
506
625
    @patch.object(unison, 'ssh_authorized_peers')
507
 
    def test_upgrade_charm_not_leader(self, ssh_authorized_peers):
508
 
        self.eligible_leader.return_value = False
 
626
    def test_upgrade_charm_not_leader(self, ssh_authorized_peers,
 
627
                                      mock_update_hash_from_path,
 
628
                                      mock_ensure_ssl_cert_master,
 
629
                                      mock_relation_ids,
 
630
                                      mock_log):
 
631
        mock_relation_ids.return_value = []
 
632
        mock_ensure_ssl_cert_master.return_value = False
 
633
        # Ensure always returns diff
 
634
        mock_update_hash_from_path.side_effect = \
 
635
            lambda hash, *args, **kwargs: hash.update(str(uuid.uuid4()))
 
636
 
 
637
        self.is_elected_leader.return_value = False
509
638
        self.filter_installed_packages.return_value = []
510
639
        hooks.upgrade_charm()
511
640
        self.assertTrue(self.apt_install.called)
512
641
        ssh_authorized_peers.assert_called_with(
513
642
            user=self.ssh_user, group='keystone',
514
643
            peer_interface='cluster', ensure_local_user=True)
515
 
        self.assertTrue(self.synchronize_ca.called)
516
644
        self.assertFalse(self.log.called)
517
645
        self.assertFalse(self.ensure_initial_admin.called)