~curtin-dev/curtin/bionic

« back to all changes in this revision

Viewing changes to tests/unittests/test_curthooks.py

  • Committer: Scott Moser
  • Date: 2017-08-03 19:51:16 UTC
  • mfrom: (1.1.50)
  • Revision ID: smoser@ubuntu.com-20170803195116-0xc6onji18peerm5
* New upstream snapshot.
  - tests: Add CiTestCase common parent for all curtin tests.
  - vmtests: Remove force flag for centos curthooks
  - tools/jenkins-runner: improve tgtd cleanup logic
  - tests: Drop EOL Wily Vivid and Yakkety tests.
  - Disable yum plugins when installing packages, update ca-certs for https
  - Rename centos_network_curthooks -> centos_apply_network_config.
  - tests: in centos_defaults use write_files for grub serial.
  - write_files: write files after extract, change write_files signature.
  - pass network configuration through to target for ubuntu and centos
  - tests: disable yakkety tests.
  - tools/launch: automatically pass on proxy settings to curtin
  - Add top level 'proxy' to config, deprecate top level http_proxy.
  - tools/curtainer: fix to enable deb-src for -proposed.
  - Use unshare to put chroot commands in own pid namespace.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import os
2
 
from unittest import TestCase
3
2
from mock import call, patch, MagicMock
4
 
import shutil
5
 
import tempfile
6
3
 
7
4
from curtin.commands import curthooks
8
5
from curtin import util
9
6
from curtin import config
10
7
from curtin.reporter import events
11
 
 
12
 
 
13
 
class CurthooksBase(TestCase):
14
 
    def setUp(self):
15
 
        super(CurthooksBase, self).setUp()
16
 
 
17
 
    def add_patch(self, target, attr, autospec=True):
18
 
        """Patches specified target object and sets it as attr on test
19
 
        instance also schedules cleanup"""
20
 
        m = patch(target, autospec=autospec)
21
 
        p = m.start()
22
 
        self.addCleanup(m.stop)
23
 
        setattr(self, attr, p)
24
 
 
25
 
 
26
 
class TestGetFlashKernelPkgs(CurthooksBase):
 
8
from .helpers import CiTestCase
 
9
 
 
10
 
 
11
class TestGetFlashKernelPkgs(CiTestCase):
27
12
    def setUp(self):
28
13
        super(TestGetFlashKernelPkgs, self).setUp()
29
14
        self.add_patch('curtin.util.subp', 'mock_subp')
57
42
        self.mock_is_uefi_bootable.assert_called_once_with()
58
43
 
59
44
 
60
 
class TestCurthooksInstallKernel(CurthooksBase):
 
45
class TestCurthooksInstallKernel(CiTestCase):
61
46
    def setUp(self):
62
47
        super(TestCurthooksInstallKernel, self).setUp()
63
48
        self.add_patch('curtin.util.has_pkg_available', 'mock_haspkg')
70
55
                                      'fallback-package': 'mock-fallback',
71
56
                                      'mapping': {}}}
72
57
        # Tests don't actually install anything so we just need a name
73
 
        self.target = tempfile.mktemp()
 
58
        self.target = self.tmp_dir()
74
59
 
75
60
    def test__installs_flash_kernel_packages_when_needed(self):
76
61
        kernel_package = self.kernel_cfg.get('kernel', {}).get('package', {})
94
79
            [kernel_package], target=self.target)
95
80
 
96
81
 
97
 
class TestUpdateInitramfs(CurthooksBase):
 
82
class TestUpdateInitramfs(CiTestCase):
98
83
    def setUp(self):
99
84
        super(TestUpdateInitramfs, self).setUp()
100
85
        self.add_patch('curtin.util.subp', 'mock_subp')
101
 
        self.target = tempfile.mkdtemp()
102
 
 
103
 
    def tearDown(self):
104
 
        shutil.rmtree(self.target)
 
86
        self.target = self.tmp_dir()
105
87
 
106
88
    def _mnt_call(self, point):
107
89
        target = os.path.join(self.target, point)
134
116
        self.mock_subp.assert_has_calls(subp_calls)
135
117
 
136
118
 
137
 
class TestInstallMissingPkgs(CurthooksBase):
 
119
class TestInstallMissingPkgs(CiTestCase):
138
120
    def setUp(self):
139
121
        super(TestInstallMissingPkgs, self).setUp()
140
122
        self.add_patch('platform.machine', 'mock_machine')
176
158
        self.assertEqual([], self.mock_install_packages.call_args_list)
177
159
 
178
160
 
179
 
class TestSetupGrub(CurthooksBase):
 
161
class TestSetupZipl(CiTestCase):
 
162
 
 
163
    def setUp(self):
 
164
        super(TestSetupZipl, self).setUp()
 
165
        self.target = self.tmp_dir()
 
166
 
 
167
    @patch('curtin.block.get_devices_for_mp')
 
168
    @patch('platform.machine')
 
169
    def test_noop_non_s390x(self, m_machine, m_get_devices):
 
170
        m_machine.return_value = 'non-s390x'
 
171
        curthooks.setup_zipl(None, self.target)
 
172
        self.assertEqual(0, m_get_devices.call_count)
 
173
 
 
174
    @patch('curtin.block.get_devices_for_mp')
 
175
    @patch('platform.machine')
 
176
    def test_setup_zipl_writes_etc_zipl_conf(self, m_machine, m_get_devices):
 
177
        m_machine.return_value = 's390x'
 
178
        m_get_devices.return_value = ['/dev/mapper/ubuntu--vg-root']
 
179
        curthooks.setup_zipl(None, self.target)
 
180
        m_get_devices.assert_called_with(self.target)
 
181
        with open(os.path.join(self.target, 'etc', 'zipl.conf')) as stream:
 
182
            content = stream.read()
 
183
        self.assertIn(
 
184
            '# This has been modified by the MAAS curtin installer',
 
185
            content)
 
186
 
 
187
 
 
188
class TestSetupGrub(CiTestCase):
180
189
 
181
190
    def setUp(self):
182
191
        super(TestSetupGrub, self).setUp()
183
 
        self.target = tempfile.mkdtemp()
 
192
        self.target = self.tmp_dir()
184
193
        self.add_patch('curtin.util.lsb_release', 'mock_lsb_release')
185
194
        self.mock_lsb_release.return_value = {
186
195
            'codename': 'xenial',
203
212
        self.mock_in_chroot_subp.side_effect = iter(self.in_chroot_subp_output)
204
213
        self.mock_chroot.return_value = self.mock_in_chroot
205
214
 
206
 
    def tearDown(self):
207
 
        shutil.rmtree(self.target)
208
 
 
209
215
    def test_uses_old_grub_install_devices_in_cfg(self):
210
216
        cfg = {
211
217
            'grub_install_devices': ['/dev/vdb']
434
440
            self.mock_in_chroot_subp.call_args_list[0][0])
435
441
 
436
442
 
437
 
class TestUbuntuCoreHooks(CurthooksBase):
 
443
class TestUbuntuCoreHooks(CiTestCase):
438
444
    def setUp(self):
439
445
        super(TestUbuntuCoreHooks, self).setUp()
440
446
        self.target = None
441
447
 
442
 
    def tearDown(self):
443
 
        if self.target:
444
 
            shutil.rmtree(self.target)
445
 
 
446
448
    def test_target_is_ubuntu_core(self):
447
 
        self.target = tempfile.mkdtemp()
 
449
        self.target = self.tmp_dir()
448
450
        ubuntu_core_path = os.path.join(self.target, 'system-data',
449
451
                                        'var/lib/snapd')
450
452
        util.ensure_dir(ubuntu_core_path)
457
459
        self.assertFalse(is_core)
458
460
 
459
461
    def test_target_is_ubuntu_core_noncore_target(self):
460
 
        self.target = tempfile.mkdtemp()
 
462
        self.target = self.tmp_dir()
461
463
        non_core_path = os.path.join(self.target, 'curtin')
462
464
        util.ensure_dir(non_core_path)
463
465
        self.assertTrue(os.path.isdir(non_core_path))
469
471
    @patch('curtin.commands.curthooks.handle_cloudconfig')
470
472
    def test_curthooks_no_config(self, mock_handle_cc, mock_del_file,
471
473
                                 mock_write_file):
472
 
        self.target = tempfile.mkdtemp()
 
474
        self.target = self.tmp_dir()
473
475
        cfg = {}
474
476
        curthooks.ubuntu_core_curthooks(cfg, target=self.target)
475
477
        self.assertEqual(len(mock_handle_cc.call_args_list), 0)
478
480
 
479
481
    @patch('curtin.commands.curthooks.handle_cloudconfig')
480
482
    def test_curthooks_cloud_config_remove_disabled(self, mock_handle_cc):
481
 
        self.target = tempfile.mkdtemp()
 
483
        self.target = self.tmp_dir()
482
484
        uc_cloud = os.path.join(self.target, 'system-data', 'etc/cloud')
483
485
        cc_disabled = os.path.join(uc_cloud, 'cloud-init.disabled')
484
486
        cc_path = os.path.join(uc_cloud, 'cloud.cfg.d')
496
498
        curthooks.ubuntu_core_curthooks(cfg, target=self.target)
497
499
 
498
500
        mock_handle_cc.assert_called_with(cfg.get('cloudconfig'),
499
 
                                          target=cc_path)
 
501
                                          base_dir=cc_path)
500
502
        self.assertFalse(os.path.exists(cc_disabled))
501
503
 
502
504
    @patch('curtin.util.write_file')
504
506
    @patch('curtin.commands.curthooks.handle_cloudconfig')
505
507
    def test_curthooks_cloud_config(self, mock_handle_cc, mock_del_file,
506
508
                                    mock_write_file):
507
 
        self.target = tempfile.mkdtemp()
 
509
        self.target = self.tmp_dir()
508
510
        cfg = {
509
511
            'cloudconfig': {
510
512
                'file1': {
518
520
        cc_path = os.path.join(self.target,
519
521
                               'system-data/etc/cloud/cloud.cfg.d')
520
522
        mock_handle_cc.assert_called_with(cfg.get('cloudconfig'),
521
 
                                          target=cc_path)
 
523
                                          base_dir=cc_path)
522
524
        self.assertEqual(len(mock_write_file.call_args_list), 0)
523
525
 
524
526
    @patch('curtin.util.write_file')
526
528
    @patch('curtin.commands.curthooks.handle_cloudconfig')
527
529
    def test_curthooks_net_config(self, mock_handle_cc, mock_del_file,
528
530
                                  mock_write_file):
529
 
        self.target = tempfile.mkdtemp()
 
531
        self.target = self.tmp_dir()
530
532
        cfg = {
531
533
            'network': {
532
534
                'version': '1',
541
543
        netcfg_path = os.path.join(self.target,
542
544
                                   'system-data',
543
545
                                   'etc/cloud/cloud.cfg.d',
544
 
                                   '50-network-config.cfg')
 
546
                                   '50-curtin-networking.cfg')
545
547
        netcfg = config.dump_config({'network': cfg.get('network')})
546
548
        mock_write_file.assert_called_with(netcfg_path,
547
549
                                           content=netcfg)
548
550
        self.assertEqual(len(mock_del_file.call_args_list), 0)
549
551
 
550
 
    @patch('curtin.commands.curthooks.write_files')
 
552
    @patch('curtin.commands.curthooks.futil.write_files')
551
553
    def test_handle_cloudconfig(self, mock_write_files):
552
554
        cc_target = "tmpXXXX/systemd-data/etc/cloud/cloud.cfg.d"
553
555
        cloudconfig = {
561
563
        }
562
564
 
563
565
        expected_cfg = {
564
 
            'write_files': {
565
 
                'file1': {
566
 
                    'path': '50-cloudconfig-file1.cfg',
567
 
                    'content': cloudconfig['file1']['content']},
568
 
                'foobar': {
569
 
                    'path': '50-cloudconfig-foobar.cfg',
570
 
                    'content': cloudconfig['foobar']['content']}
571
 
            }
 
566
            'file1': {
 
567
                'path': '50-cloudconfig-file1.cfg',
 
568
                'content': cloudconfig['file1']['content']},
 
569
            'foobar': {
 
570
                'path': '50-cloudconfig-foobar.cfg',
 
571
                'content': cloudconfig['foobar']['content']}
572
572
        }
573
 
        curthooks.handle_cloudconfig(cloudconfig, target=cc_target)
 
573
        curthooks.handle_cloudconfig(cloudconfig, base_dir=cc_target)
574
574
        mock_write_files.assert_called_with(expected_cfg, cc_target)
575
575
 
576
576
    def test_handle_cloudconfig_bad_config(self):
577
577
        with self.assertRaises(ValueError):
578
 
            curthooks.handle_cloudconfig([], target="foobar")
 
578
            curthooks.handle_cloudconfig([], base_dir="foobar")
 
579
 
 
580
 
 
581
class TestDetectRequiredPackages(CiTestCase):
 
582
    test_config = {
 
583
        'storage': {
 
584
            1: {
 
585
                'bcache': {
 
586
                    'type': 'bcache', 'name': 'bcache0', 'id': 'cache0',
 
587
                    'backing_device': 'sda3', 'cache_device': 'sdb'},
 
588
                'lvm_partition': {
 
589
                    'id': 'lvol1', 'name': 'lv1', 'volgroup': 'vg1',
 
590
                    'type': 'lvm_partition'},
 
591
                'lvm_volgroup': {
 
592
                    'id': 'vol1', 'name': 'vg1', 'devices': ['sda', 'sdb'],
 
593
                    'type': 'lvm_volgroup'},
 
594
                'raid': {
 
595
                    'id': 'mddevice', 'name': 'md0', 'type': 'raid',
 
596
                    'raidlevel': 5, 'devices': ['sda1', 'sdb1', 'sdc1']},
 
597
                'ext2': {
 
598
                    'id': 'format0', 'fstype': 'ext2', 'type': 'format'},
 
599
                'ext3': {
 
600
                    'id': 'format1', 'fstype': 'ext3', 'type': 'format'},
 
601
                'ext4': {
 
602
                    'id': 'format2', 'fstype': 'ext4', 'type': 'format'},
 
603
                'btrfs': {
 
604
                    'id': 'format3', 'fstype': 'btrfs', 'type': 'format'},
 
605
                'xfs': {
 
606
                    'id': 'format4', 'fstype': 'xfs', 'type': 'format'}}
 
607
        },
 
608
        'network': {
 
609
            1: {
 
610
                'bond': {
 
611
                    'name': 'bond0', 'type': 'bond',
 
612
                    'bond_interfaces': ['interface0', 'interface1'],
 
613
                    'params': {'bond-mode': 'active-backup'},
 
614
                    'subnets': [
 
615
                        {'type': 'static', 'address': '10.23.23.2/24'},
 
616
                        {'type': 'static', 'address': '10.23.24.2/24'}]},
 
617
                'vlan': {
 
618
                    'id': 'interface1.2667', 'mtu': 1500, 'name':
 
619
                    'interface1.2667', 'type': 'vlan', 'vlan_id': 2667,
 
620
                    'vlan_link': 'interface1',
 
621
                    'subnets': [{'address': '10.245.184.2/24',
 
622
                                 'dns_nameservers': [], 'type': 'static'}]},
 
623
                'bridge': {
 
624
                    'name': 'br0', 'bridge_interfaces': ['eth0', 'eth1'],
 
625
                    'type': 'bridge', 'params': {
 
626
                        'bridge_stp': 'off', 'bridge_fd': 0,
 
627
                        'bridge_maxwait': 0},
 
628
                    'subnets': [
 
629
                        {'type': 'static', 'address': '192.168.14.2/24'},
 
630
                        {'type': 'static', 'address': '2001:1::1/64'}]}},
 
631
            2: {
 
632
                'vlan': {
 
633
                    'vlans': {
 
634
                        'en-intra': {'id': 1, 'link': 'eno1', 'dhcp4': 'yes'},
 
635
                        'en-vpn': {'id': 2, 'link': 'eno1'}}},
 
636
                'bridge': {
 
637
                    'bridges': {
 
638
                        'br0': {
 
639
                            'interfaces': ['wlp1s0', 'switchports'],
 
640
                            'dhcp4': True}}}}
 
641
        },
 
642
    }
 
643
 
 
644
    def _fmt_config(self, config_items):
 
645
        res = {}
 
646
        for item, item_confs in config_items.items():
 
647
            version = item_confs['version']
 
648
            res[item] = {'version': version}
 
649
            if version == 1:
 
650
                res[item]['config'] = [self.test_config[item][version][i]
 
651
                                       for i in item_confs['items']]
 
652
            elif version == 2 and item == 'network':
 
653
                for cfg_item in item_confs['items']:
 
654
                    res[item].update(self.test_config[item][version][cfg_item])
 
655
            else:
 
656
                raise NotImplementedError
 
657
        return res
 
658
 
 
659
    def _test_req_mappings(self, req_mappings):
 
660
        for (config_items, expected_reqs) in req_mappings:
 
661
            cfg = self._fmt_config(config_items)
 
662
            actual_reqs = curthooks.detect_required_packages(cfg)
 
663
            self.assertEqual(set(actual_reqs), set(expected_reqs),
 
664
                             'failed for config: {}'.format(config_items))
 
665
 
 
666
    def test_storage_v1_detect(self):
 
667
        self._test_req_mappings((
 
668
            ({'storage': {
 
669
                'version': 1,
 
670
                'items': ('lvm_partition', 'lvm_volgroup', 'btrfs', 'xfs')}},
 
671
             ('lvm2', 'xfsprogs', 'btrfs-tools')),
 
672
            ({'storage': {
 
673
                'version': 1,
 
674
                'items': ('raid', 'bcache', 'ext3', 'xfs')}},
 
675
             ('mdadm', 'bcache-tools', 'e2fsprogs', 'xfsprogs')),
 
676
            ({'storage': {
 
677
                'version': 1,
 
678
                'items': ('raid', 'lvm_volgroup', 'lvm_partition', 'ext3',
 
679
                          'ext4', 'btrfs')}},
 
680
             ('lvm2', 'mdadm', 'e2fsprogs', 'btrfs-tools')),
 
681
            ({'storage': {
 
682
                'version': 1,
 
683
                'items': ('bcache', 'lvm_volgroup', 'lvm_partition', 'ext2')}},
 
684
             ('bcache-tools', 'lvm2', 'e2fsprogs')),
 
685
        ))
 
686
 
 
687
    def test_network_v1_detect(self):
 
688
        self._test_req_mappings((
 
689
            ({'network': {
 
690
                'version': 1,
 
691
                'items': ('bridge',)}},
 
692
             ('bridge-utils',)),
 
693
            ({'network': {
 
694
                'version': 1,
 
695
                'items': ('vlan', 'bond')}},
 
696
             ('vlan', 'ifenslave')),
 
697
            ({'network': {
 
698
                'version': 1,
 
699
                'items': ('bond', 'bridge')}},
 
700
             ('ifenslave', 'bridge-utils')),
 
701
            ({'network': {
 
702
                'version': 1,
 
703
                'items': ('vlan', 'bridge', 'bond')}},
 
704
             ('ifenslave', 'bridge-utils', 'vlan')),
 
705
        ))
 
706
 
 
707
    def test_mixed_v1_detect(self):
 
708
        self._test_req_mappings((
 
709
            ({'storage': {
 
710
                'version': 1,
 
711
                'items': ('raid', 'bcache', 'ext4')},
 
712
              'network': {
 
713
                  'version': 1,
 
714
                  'items': ('vlan',)}},
 
715
             ('mdadm', 'bcache-tools', 'e2fsprogs', 'vlan')),
 
716
            ({'storage': {
 
717
                'version': 1,
 
718
                'items': ('lvm_partition', 'lvm_volgroup', 'xfs')},
 
719
              'network': {
 
720
                  'version': 1,
 
721
                  'items': ('bridge', 'bond')}},
 
722
             ('lvm2', 'xfsprogs', 'bridge-utils', 'ifenslave')),
 
723
            ({'storage': {
 
724
                'version': 1,
 
725
                'items': ('ext3', 'ext4', 'btrfs')},
 
726
              'network': {
 
727
                  'version': 1,
 
728
                  'items': ('bond', 'vlan')}},
 
729
             ('e2fsprogs', 'btrfs-tools', 'vlan', 'ifenslave')),
 
730
        ))
 
731
 
 
732
    def test_network_v2_detect(self):
 
733
        self._test_req_mappings((
 
734
            ({'network': {
 
735
                'version': 2,
 
736
                'items': ('bridge',)}},
 
737
             ('bridge-utils',)),
 
738
            ({'network': {
 
739
                'version': 2,
 
740
                'items': ('vlan',)}},
 
741
             ('vlan',)),
 
742
            ({'network': {
 
743
                'version': 2,
 
744
                'items': ('vlan', 'bridge')}},
 
745
             ('vlan', 'bridge-utils')),
 
746
        ))
 
747
 
 
748
    def test_mixed_storage_v1_network_v2_detect(self):
 
749
        self._test_req_mappings((
 
750
            ({'network': {
 
751
                'version': 2,
 
752
                'items': ('bridge', 'vlan')},
 
753
             'storage': {
 
754
                 'version': 1,
 
755
                 'items': ('raid', 'bcache', 'ext4')}},
 
756
             ('vlan', 'bridge-utils', 'mdadm', 'bcache-tools', 'e2fsprogs')),
 
757
        ))
 
758
 
 
759
    def test_invalid_version_in_config(self):
 
760
        with self.assertRaises(ValueError):
 
761
            curthooks.detect_required_packages({'network': {'version': 3}})
 
762
 
579
763
 
580
764
# vi: ts=4 expandtab syntax=python