Exemple #1
0
 def test_populate_bootfs_contents_content_mismatch(self):
     # If a content source ends in a slash, so must the target.
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         unpackdir = resources.enter_context(TemporaryDirectory())
         # Fast forward a state machine to the method under test.
         args = SimpleNamespace(
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             unpackdir=unpackdir,
             workdir=workdir,
             debug=None,
             cloud_init=None,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         state = resources.enter_context(XXXClassicBuilder(args))
         state._next.pop()
         state._next.append(state.populate_bootfs_contents)
         # Now we have to craft enough of gadget definition to drive the
         # method under test.  The two paths (is-a-file and is-a-directory)
         # are differentiated by whether the source ends in a slash or not.
         # In that case, the target must also end in a slash.
         content1 = SimpleNamespace(
             source='bs/',
             # No slash!
             target='bt',
         )
         part = SimpleNamespace(
             role=StructureRole.system_boot,
             filesystem=FileSystemType.ext4,
             content=[content1],
         )
         volume = SimpleNamespace(
             structures=[part],
             bootloader=BootLoader.grub,
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         # Since we're not running make_temporary_directories(), just set
         # up some additional expected state.
         state.unpackdir = unpackdir
         prep_state(state, workdir)
         # Run the state machine.  Don't blat to stderr.
         resources.enter_context(patch('ubuntu_image.state.log'))
         with self.assertRaises(ValueError) as cm:
             next(state)
         self.assertEqual(str(cm.exception),
                          'target must end in a slash: bt')
Exemple #2
0
 def test_populate_rootfs_contents_without_cloud_init(self):
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         cloud_init = resources.enter_context(
             NamedTemporaryFile('w', encoding='utf-8'))
         print('cloud init user data', end='', flush=True, file=cloud_init)
         args = SimpleNamespace(
             cmd='classic',
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             workdir=workdir,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             cloud_init=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             disk_info=None,
             disable_console_conf=False,
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         state = resources.enter_context(XXXClassicBuilder(args))
         # Now we have to craft enough of gadget definition to drive the
         # method under test.
         part = SimpleNamespace(
             role=StructureRole.system_boot,
             filesystem_label='system-boot',
             filesystem=FileSystemType.none,
         )
         volume = SimpleNamespace(
             structures=[part],
             bootloader=BootLoader.uboot,
             schema=VolumeSchema.mbr,
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         prep_state(state, workdir)
         # Fake some state expected by the method under test.
         state.unpackdir = resources.enter_context(TemporaryDirectory())
         os.makedirs(os.path.join(state.unpackdir, 'chroot'))
         state.rootfs = resources.enter_context(TemporaryDirectory())
         # Jump right to the state method we're trying to test.
         state._next.pop()
         state._next.append(state.populate_rootfs_contents)
         next(state)
         # The user data should not have been written and there should be
         # no metadata either.
         seed_path = os.path.join(state.rootfs, 'var', 'lib', 'cloud',
                                  'seed', 'nocloud-net')
         self.assertFalse(
             os.path.exists(os.path.join(seed_path, 'user-data')))
         self.assertFalse(
             os.path.exists(os.path.join(seed_path, 'meta-data')))
    def test_generate_manifests_exclude(self):
        # This is not a full test of the manifest generation process as this
        # requires more preparation.  Here we try to see if deprecated words
        # are being removed from the manifest.
        with ExitStack() as resources:
            workdir = resources.enter_context(TemporaryDirectory())
            unpackdir = resources.enter_context(TemporaryDirectory())
            outputdir = resources.enter_context(TemporaryDirectory())
            # Fast forward a state machine to the method under test.
            args = SimpleNamespace(
                project='ubuntu-cpc',
                suite='xenial',
                arch='amd64',
                image_format='img',
                unpackdir=unpackdir,
                workdir=workdir,
                debug=True,
                cloud_init=None,
                output=None,
                subproject=None,
                subarch=None,
                output_dir=outputdir,
                with_proposed=None,
                extra_ppas=None,
                hooks_directory=[],
                gadget_tree=self.gadget_tree,
            )
            # Jump right to the method under test.
            state = resources.enter_context(XXXClassicBuilder(args))
            state._next.pop()
            state._next.append(state.generate_manifests)
            # Set up expected state.
            state.rootfs = os.path.join(workdir, 'root')
            test_output = dedent("""\
                                 foo 1.1
                                 bar 3.12.3-0ubuntu1
                                 ubiquity 17.10.8
                                 baz 2.3
                                 casper 1.384
                                 """)

            def run_script(command, *, check=True, **args):
                stdout = args.pop('stdout', PIPE)
                stdout.write(test_output)
                stdout.flush()

            resources.enter_context(
                patch('ubuntu_image.classic_builder.run',
                      side_effect=run_script))
            next(state)
            manifest_path = os.path.join(outputdir, 'filesystem.manifest')
            self.assertTrue(os.path.exists(manifest_path))
            with open(manifest_path) as f:
                self.assertEqual(
                    f.read(),
                    dedent("""\
                           foo 1.1
                           bar 3.12.3-0ubuntu1
                           baz 2.3
                           """))
Exemple #4
0
 def test_populate_rootfs_contents_from_filesystem(self):
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         args = SimpleNamespace(
             cmd='classic',
             project=None,
             suite='xenial',
             arch='amd64',
             image_format='img',
             workdir=workdir,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             cloud_init=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             disk_info=None,
             disable_console_conf=False,
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         state = resources.enter_context(XXXClassicBuilder(args))
         # Now we have to craft enough of gadget definition to drive the
         # method under test.
         part = SimpleNamespace(
             role=StructureRole.system_data,
             filesystem_label='writable',
             filesystem=FileSystemType.none,
         )
         volume = SimpleNamespace(
             structures=[part],
             bootloader=BootLoader.grub,
             schema=VolumeSchema.gpt,
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         prep_state(state, workdir)
         # Fake some state expected by the method under test.
         args.filesystem = resources.enter_context(TemporaryDirectory())
         etc_path = os.path.join(args.filesystem, 'etc')
         os.makedirs(etc_path)
         with open(os.path.join(etc_path, 'fstab'), 'w') as fp:
             fp.write('LABEL=cloudimg-rootfs   /    ext4   defaults    0 0')
         state.rootfs = resources.enter_context(TemporaryDirectory())
         # Jump right to the state method we're trying to test.
         state._next.pop()
         state._next.append(state.populate_rootfs_contents)
         next(state)
         # The seed metadata should exist.
         # And the filesystem label should be modified to 'writable'
         fstab_data = os.path.join(state.rootfs, 'etc', 'fstab')
         with open(fstab_data, 'r', encoding='utf-8') as fp:
             self.assertEqual(
                 fp.read(), 'LABEL=writable   '
                 '/    ext4   defaults    0 0')
Exemple #5
0
 def test_populate_rootfs_contents_grub_boot_remove(self):
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         args = SimpleNamespace(
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             workdir=workdir,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             cloud_init=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         state = resources.enter_context(XXXClassicBuilder(args))
         # Now we have to craft enough of gadget definition to drive the
         # method under test.
         part = SimpleNamespace(
             role=StructureRole.system_boot,
             filesystem_label='system-boot',
             filesystem=FileSystemType.none,
         )
         volume = SimpleNamespace(
             structures=[part],
             bootloader=BootLoader.uboot,
             schema=VolumeSchema.mbr,
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         prep_state(state, workdir)
         # Fake some state expected by the method under test.
         state.unpackdir = resources.enter_context(TemporaryDirectory())
         os.makedirs(os.path.join(state.unpackdir, 'chroot'))
         state.rootfs = resources.enter_context(TemporaryDirectory())
         # Create some dummy files in the grub directory.
         grub_dir = os.path.join(state.rootfs, 'boot', 'grub')
         os.makedirs(grub_dir, exist_ok=True)
         grub_inside_dir = os.path.join(grub_dir, 'dir')
         os.makedirs(grub_inside_dir, exist_ok=True)
         grub_file = os.path.join(grub_dir, 'test')
         open(grub_file, 'wb').close()
         # Jump right to the state method we're trying to test.
         state._next.pop()
         state._next.append(state.populate_rootfs_contents)
         next(state)
         # /boot/grub should persist, but not the files inside
         self.assertTrue(os.path.exists(grub_dir))
         self.assertFalse(os.path.exists(grub_inside_dir))
         self.assertFalse(os.path.exists(grub_file))
Exemple #6
0
 def test_bootloader_options_invalid(self):
     # This test provides coverage for populate_bootfs_contents() when the
     # bootloader has a bogus value.
     #
     # We don't want to run the entire state machine just for this test, so
     # we start by setting up enough of the environment for the method
     # under test to function.
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         # Fast forward a state machine to the method under test.
         args = SimpleNamespace(
             cmd='classic',
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             workdir=workdir,
             debug=None,
             cloud_init=None,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             disk_info=None,
             disable_console_conf=False,
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         state = resources.enter_context(XXXClassicBuilder(args))
         state._next.pop()
         state._next.append(state.populate_bootfs_contents)
         # Now we have to craft enough of gadget definition to drive the
         # method under test.
         part = SimpleNamespace(
             role=StructureRole.system_boot,
             filesystem_label='system-boot',
             filesystem=FileSystemType.none,
         )
         volume = SimpleNamespace(
             structures=[part],
             bootloader='bogus',
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         prep_state(state, workdir)
         # Don't blat to stderr.
         resources.enter_context(patch('ubuntu_image.state.log'))
         with self.assertRaises(ValueError) as cm:
             next(state)
         self.assertEqual(str(cm.exception),
                          'Unsupported volume bootloader value: bogus')
Exemple #7
0
 def test_live_build_command_fails_debug(self):
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         unpackdir = resources.enter_context(TemporaryDirectory())
         # Fast forward a state machine to the method under test.
         args = SimpleNamespace(
             cmd='classic',
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             unpackdir=unpackdir,
             workdir=workdir,
             debug=True,
             cloud_init=None,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             disk_info=None,
             disable_console_conf=False,
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         # Jump right to the method under test.
         state = resources.enter_context(XXXClassicBuilder(args))
         state.unpackdir = unpackdir
         state._next.pop()
         state._next.append(state.prepare_image)
         resources.enter_context(
             patch('ubuntu_image.helpers.subprocess_run',
                   return_value=SimpleNamespace(
                       returncode=1,
                       stdout='command stdout',
                       stderr='command stderr',
                       check_returncode=check_returncode,
                   )))
         log_capture = resources.enter_context(LogCapture())
         next(state)
         self.assertEqual(state.exitcode, 1)
         # Note that there is traceback in the output now.
         self.assertEqual(log_capture.logs, [
             (logging.ERROR,
              'COMMAND FAILED: dpkg -L livecd-rootfs | grep "auto$"'),
             (logging.ERROR, 'command stdout'),
             (logging.ERROR, 'command stderr'),
             (logging.ERROR, 'Full debug traceback follows'),
             ('IMAGINE THE TRACEBACK HERE'),
         ])
Exemple #8
0
 def test_fs_contents(self):
     # Run the action classic builder through the steps needed to
     # at least call `lb config && lb build`.
     output = self._resources.enter_context(NamedTemporaryFile())
     workdir = self._resources.enter_context(TemporaryDirectory())
     unpackdir = os.path.join(workdir, 'unpack')
     mock = LiveBuildMocker(unpackdir)
     args = SimpleNamespace(
         cmd='classic',
         project='ubuntu-cpc',
         suite='xenial',
         arch='amd64',
         image_format='img',
         output=output.name,
         subproject='subproject',
         subarch='subarch',
         output_dir=None,
         workdir=workdir,
         cloud_init=None,
         with_proposed='1',
         extra_ppas='******',
         hooks_directory=[],
         disk_info=None,
         disable_console_conf=False,
         gadget_tree=self.gadget_tree,
         filesystem=None,
     )
     state = self._resources.enter_context(XXXClassicBuilder(args))
     # Mock out rootfs generation `live_build`
     # and create dummy top-level filesystem layout.
     self._resources.enter_context(
         patch('ubuntu_image.helpers.run', mock.run))
     state.run_thru('populate_bootfs_contents')
     # How does the root and boot file systems look?
     files = [
         '{boot}/EFI/boot/bootx64.efi',
         '{boot}/EFI/boot/grubx64.efi',
         '{boot}/EFI/ubuntu/grub.cfg',
         '{root}/boot/',
     ]
     for filename in files:
         path = filename.format(
             root=state.rootfs,
             boot=state.gadget.volumes['pc'].bootfs,
         )
         self.assertTrue(os.path.exists(path), path)
     # Simply check if all top-level files and folders exist.
     for dirname in DIRS_UNDER_ROOTFS:
         path = os.path.join(state.rootfs, dirname)
         self.assertTrue(os.path.exists(path), path)
 def test_live_build_pass_arguments(self):
     with ExitStack() as resources:
         argstoenv = {
             'project': 'PROJECT',
             'suite': 'SUITE',
             'arch': 'ARCH',
             'subproject': 'SUBPROJECT',
             'subarch': 'SUBARCH',
             'with_proposed': 'PROPOSED',
             'extra_ppas': '******',
         }
         kwargs_skel = {
             'workdir': '/tmp',
             'output_dir': '/tmp',
             'hooks_directory': '/tmp',
             'output': None,
             'cloud_init': None,
             'gadget_tree': None,
             'unpackdir': None,
             'debug': None,
             'project': None,
             'suite': None,
             'arch': None,
             'subproject': None,
             'subarch': None,
             'with_proposed': None,
             'extra_ppas': None,
         }
         for arg, env in argstoenv.items():
             kwargs = dict(kwargs_skel)
             kwargs[arg] = 'test'
             args = SimpleNamespace(**kwargs)
             # Jump right to the method under test.
             state = resources.enter_context(XXXClassicBuilder(args))
             state._next.pop()
             state._next.append(state.prepare_image)
             mock = resources.enter_context(
                 patch('ubuntu_image.classic_builder.live_build'))
             next(state)
             self.assertEqual(len(mock.call_args_list), 1)
             posargs, kwargs = mock.call_args_list[0]
             self.assertIn(env, posargs[1])
             self.assertEqual(posargs[1][env], 'test')
Exemple #10
0
 def test_prepare_gadget_tree_locally(self):
     # Run the action classic builder through the steps needed to
     # at least call `snapcraft prime`.
     # To create pc-boot.img and pc-core.img, we need to fetch
     # packages like grub-pc-bin, shim-signed from ubuntu archive,
     # even if gadget tree is placed locally on the machine.
     workdir = self._resources.enter_context(TemporaryDirectory())
     args = SimpleNamespace(
         cmd='classic',
         project='ubuntu-cpc',
         suite='xenial',
         arch='amd64',
         image_format='img',
         output=None,
         subproject=None,
         subarch=None,
         output_dir=None,
         workdir=workdir,
         cloud_init=None,
         with_with_proposed=None,
         extra_ppas=None,
         hooks_directory=[],
         disk_info=None,
         disable_console_conf=False,
         gadget_tree=self.gadget_tree,
         filesystem=None,
     )
     state = self._resources.enter_context(XXXClassicBuilder(args))
     gadget_dir = os.path.join(workdir, 'unpack', 'gadget')
     state.run_thru('prepare_gadget_tree')
     files = [
         '{gadget_dir}/grub-cpc.cfg',
         '{gadget_dir}/grubx64.efi',
         '{gadget_dir}/pc-boot.img',
         '{gadget_dir}/pc-core.img',
         '{gadget_dir}/shim.efi.signed',
         '{gadget_dir}/meta/gadget.yaml',
     ]
     # Check if all needed bootloader bits are in place.
     for filename in files:
         path = filename.format(gadget_dir=gadget_dir, )
         self.assertTrue(os.path.exists(path), path)
Exemple #11
0
 def test_live_build_pass_arguments(self):
     with ExitStack() as resources:
         argstoenv = {
             'project': 'PROJECT',
             'suite': 'SUITE',
             'arch': 'ARCH',
             'subproject': 'SUBPROJECT',
             'subarch': 'SUBARCH',
             'with_proposed': 'PROPOSED',
         }
         kwargs_skel = {
             'workdir': '/tmp',
             'output_dir': '/tmp',
             'hooks_directory': '/tmp',
             'output': None,
             'cloud_init': None,
             'gadget_tree': None,
             'unpackdir': None,
             'debug': None,
             'project': None,
             'suite': None,
             'arch': None,
             'subproject': None,
             'subarch': None,
             'with_proposed': None,
             'extra_ppas': None,
             'filesystem': None,
         }
         for arg, env in argstoenv.items():
             kwargs = dict(kwargs_skel)
             kwargs[arg] = 'test' if arg != 'with_proposed' else True
             args = SimpleNamespace(**kwargs)
             # Jump right to the method under test.
             state = resources.enter_context(XXXClassicBuilder(args))
             state._next.pop()
             state._next.append(state.prepare_image)
             mock = resources.enter_context(
                 patch('ubuntu_image.classic_builder.live_build'))
             next(state)
             self.assertEqual(len(mock.call_args_list), 1)
             posargs, kwargs = mock.call_args_list[0]
             self.assertIn(env, posargs[1])
             self.assertEqual(posargs[1][env],
                              'test' if arg != 'with_proposed' else '1')
         # The extra_ppas argument is actually a list, so it needs a
         # separate test-case.
         outputtoinput = {
             'foo/bar': ['foo/bar'],
             'foo/bar foo/baz': ['foo/bar', 'foo/baz'],
         }
         for outputarg, inputarg in outputtoinput.items():
             kwargs = dict(kwargs_skel)
             kwargs['extra_ppas'] = inputarg
             args = SimpleNamespace(**kwargs)
             # Jump right to the method under test.
             state = resources.enter_context(XXXClassicBuilder(args))
             state._next.pop()
             state._next.append(state.prepare_image)
             mock = resources.enter_context(
                 patch('ubuntu_image.classic_builder.live_build'))
             next(state)
             self.assertEqual(len(mock.call_args_list), 1)
             posargs, kwargs = mock.call_args_list[0]
             self.assertIn('EXTRA_PPAS', posargs[1])
             self.assertEqual(posargs[1]['EXTRA_PPAS'], outputarg)
Exemple #12
0
 def test_populate_filesystems_none_type(self):
     # We do a bit-wise copy when the file system has no type.
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         unpackdir = resources.enter_context(TemporaryDirectory())
         # Fast forward a state machine to the method under test.
         args = SimpleNamespace(
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             unpackdir=unpackdir,
             workdir=workdir,
             debug=None,
             cloud_init=None,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         # Jump right to the method under test.
         state = resources.enter_context(XXXClassicBuilder(args))
         state._next.pop()
         state._next.append(state.populate_filesystems)
         # Set up expected state.
         state.unpackdir = unpackdir
         state.images = os.path.join(workdir, '.images')
         os.makedirs(state.images)
         part0_img = os.path.join(state.images, 'part0.img')
         # Craft a gadget specification.
         contents1 = SimpleNamespace(
             image='image1.img',
             size=None,
             offset=None,
         )
         contents2 = SimpleNamespace(
             image='image2.img',
             size=23,
             offset=None,
         )
         contents3 = SimpleNamespace(
             image='image3.img',
             size=None,
             offset=None,
         )
         contents4 = SimpleNamespace(
             image='image4.img',
             size=None,
             offset=127,
         )
         part = SimpleNamespace(
             role=None,
             filesystem=FileSystemType.none,
             content=[contents1, contents2, contents3, contents4],
             size=150,
         )
         volume = SimpleNamespace(
             structures=[part],
             schema=VolumeSchema.gpt,
             bootloader=BootLoader.grub,
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         prep_state(state, workdir, [part0_img])
         # The source image.
         gadget_dir = os.path.join(unpackdir, 'gadget')
         os.makedirs(gadget_dir)
         with open(os.path.join(gadget_dir, 'image1.img'), 'wb') as fp:
             fp.write(b'\1' * 47)
         with open(os.path.join(gadget_dir, 'image2.img'), 'wb') as fp:
             fp.write(b'\2' * 19)
         with open(os.path.join(gadget_dir, 'image3.img'), 'wb') as fp:
             fp.write(b'\3' * 51)
         with open(os.path.join(gadget_dir, 'image4.img'), 'wb') as fp:
             fp.write(b'\4' * 11)
         # Mock out the mkfs.ext4 call, and we'll just test the contents
         # directory (i.e. what would go in the ext4 file system).
         resources.enter_context(
             patch('ubuntu_image.common_builder.mkfs_ext4'))
         next(state)
         # Check the contents of the part0 image file.
         with open(part0_img, 'rb') as fp:
             data = fp.read()
         self.assertEqual(
             data,
             b'\1' * 47 + b'\2' * 19 +
             # 23 (specified size) - 19 (actual size).
             b'\0' * 4 + b'\3' * 51 +
             # 127 (offset) - 121 (written byte count)
             b'\0' * 6 + b'\4' * 11 +
             # 150 (image size) - 138 (written byte count)
             b'\0' * 12)
Exemple #13
0
 def test_populate_bootfs_contents(self):
     # This test provides coverage for populate_bootfs_contents() when a
     # volume's part is defined as an ext4 or vfat file system type.  In
     # that case, the part's contents are copied to the target directory.
     # There are two paths here: one where the contents are a directory and
     # the other where the contents are a file.  We can test both cases
     # here for full coverage.
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         unpackdir = resources.enter_context(TemporaryDirectory())
         # Fast forward a state machine to the method under test.
         args = SimpleNamespace(
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             unpackdir=unpackdir,
             workdir=workdir,
             cloud_init=None,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         state = resources.enter_context(XXXClassicBuilder(args))
         state._next.pop()
         state._next.append(state.populate_bootfs_contents)
         # Now we have to craft enough of gadget definition to drive the
         # method under test.  The two paths (is-a-file and is-a-directory)
         # are differentiated by whether the source ends in a slash or not.
         # In that case, the target must also end in a slash.
         contents1 = SimpleNamespace(
             source='as.dat',
             target='at.dat',
         )
         contents2 = SimpleNamespace(
             source='bs/',
             target='bt/',
         )
         part = SimpleNamespace(
             role=None,
             filesystem_label='not a boot',
             filesystem=FileSystemType.ext4,
             content=[contents1, contents2],
         )
         volume = SimpleNamespace(
             structures=[part],
             bootloader=BootLoader.grub,
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         # Since we're not running make_temporary_directories(), just set
         # up some additional expected state.
         state.unpackdir = unpackdir
         prep_state(state, workdir)
         # Run the method, the testable effects of which copy all the files
         # in the source directory (i.e. <unpackdir>/gadget/<source>) into
         # the target directory (i.e. <workdir>/part0).  So put some
         # contents into the source locations.
         gadget_dir = os.path.join(unpackdir, 'gadget')
         os.makedirs(gadget_dir)
         src = os.path.join(gadget_dir, 'as.dat')
         with open(src, 'wb') as fp:
             fp.write(b'01234')
         src = os.path.join(gadget_dir, 'bs')
         os.makedirs(src)
         # Put a couple of files and a directory in the source, since
         # directories are copied recursively.
         with open(os.path.join(src, 'c.dat'), 'wb') as fp:
             fp.write(b'56789')
         srcdir = os.path.join(src, 'd')
         os.makedirs(srcdir)
         with open(os.path.join(srcdir, 'e.dat'), 'wb') as fp:
             fp.write(b'0abcd')
         # Run the state machine.
         next(state)
         # Did all the files and directories get copied?
         dstbase = os.path.join(workdir, 'volumes', 'volume1', 'part0')
         with open(os.path.join(dstbase, 'at.dat'), 'rb') as fp:
             self.assertEqual(fp.read(), b'01234')
         with open(os.path.join(dstbase, 'bt', 'c.dat'), 'rb') as fp:
             self.assertEqual(fp.read(), b'56789')
         with open(os.path.join(dstbase, 'bt', 'd', 'e.dat'), 'rb') as fp:
             self.assertEqual(fp.read(), b'0abcd')
Exemple #14
0
 def test_workaround_sparse_swapfile(self):
     # Test if the swapfile unsparsing workaround is fired.
     with ExitStack() as resources:
         workdir = resources.enter_context(TemporaryDirectory())
         unpackdir = resources.enter_context(TemporaryDirectory())
         # Fast forward a state machine to the method under test.
         args = SimpleNamespace(
             cmd='classic',
             project='ubuntu-cpc',
             suite='xenial',
             arch='amd64',
             image_format='img',
             unpackdir=unpackdir,
             workdir=workdir,
             debug=None,
             cloud_init=None,
             output=None,
             subproject=None,
             subarch=None,
             output_dir=None,
             with_proposed=None,
             extra_ppas=None,
             hooks_directory=[],
             disk_info=None,
             disable_console_conf=False,
             gadget_tree=self.gadget_tree,
             filesystem=None,
         )
         # Jump right to the method under test.
         state = resources.enter_context(XXXClassicBuilder(args))
         state._next.pop()
         state._next.append(state.populate_filesystems)
         # Set up expected state.
         state.unpackdir = unpackdir
         state.images = os.path.join(workdir, '.images')
         os.makedirs(state.images)
         part0_img = os.path.join(state.images, 'part0.img')
         # Craft a gadget specification.
         part = SimpleNamespace(
             role=StructureRole.system_data,
             filesystem=FileSystemType.ext4,
             filesystem_label='writable',
             size=150,
         )
         volume = SimpleNamespace(
             structures=[part],
             bootloader=BootLoader.grub,
         )
         state.gadget = SimpleNamespace(
             volumes=dict(volume1=volume),
             seeded=False,
         )
         prep_state(state, workdir, [part0_img])
         # Prepare the dummy swapfile on the rootfs
         state.rootfs = os.path.join(workdir, 'rootfs')
         os.makedirs(state.rootfs)
         swap_path = os.path.join(state.rootfs, 'swapfile')
         open(swap_path, 'w')
         # Mock out the mkfs.ext4 call, and we'll just test the contents
         # directory (i.e. what would go in the ext4 file system).
         mock_mkfs = resources.enter_context(
             patch('ubuntu_image.common_builder.mkfs_ext4'))
         mock_unsparse = resources.enter_context(
             patch('ubuntu_image.common_builder.unsparse_swapfile_ext4'))
         next(state)
         # Check that mkfs.ext4 got called, after which we tried to
         # unsparse the swapfile as expected.
         self.assertEqual(len(mock_mkfs.call_args_list), 1)
         self.assertEqual(len(mock_unsparse.call_args_list), 1)