Exemplo n.º 1
0
    def test_symlink(self):
        self._check_item(
            SymlinkToDirItem(from_target='t', source='x', dest='y'),
            {ProvidesDirectory(path='y')},
            {require_directory('/'),
             require_directory('/x')},
        )

        self._check_item(
            SymlinkToFileItem(from_target='t',
                              source='source_file',
                              dest='dest_symlink'),
            {ProvidesFile(path='dest_symlink')},
            {require_directory('/'),
             require_file('/source_file')},
        )
Exemplo n.º 2
0
 def test_make_dirs(self):
     self._check_item(
         MakeDirsItem(from_target='t', into_dir='x', path_to_make='y/z'),
         {ProvidesDirectory(path='x/y'),
          ProvidesDirectory(path='x/y/z')},
         {require_directory('x')},
     )
Exemplo n.º 3
0
    def test_tarball(self):
        with temp_filesystem() as fs_path, tempfile.TemporaryDirectory() as td:
            tar_path = os.path.join(td, 'test.tar')
            zst_path = os.path.join(td, 'test.tar.zst')

            with tarfile.TarFile(tar_path, 'w') as tar_obj:
                tar_obj.add(fs_path, filter=_tarinfo_strip_dir_prefix(fs_path))
            subprocess.check_call(['zstd', tar_path, '-o', zst_path])

            for path in (tar_path, zst_path):
                self._check_item(
                    _tarball_item(path, 'y'),
                    temp_filesystem_provides('y'),
                    {require_directory('y')},
                )

            # Test a hash validation failure, follows the item above
            with self.assertRaisesRegex(AssertionError, 'failed hash vali'):
                image_source_item(
                    TarballItem,
                    exit_stack=None,
                    layer_opts=DUMMY_LAYER_OPTS,
                )(
                    from_target='t',
                    into_dir='y',
                    source={
                        'source': tar_path,
                        'content_hash': 'sha256:deadbeef',
                    },
                    force_root_ownership=False,
                )
Exemplo n.º 4
0
    def test_clone_special_files(self):
        with TempSubvolumes(sys.argv[0]) as temp_subvols:
            src_subvol = temp_subvols.create('test_clone_special_files_src')
            dest_subvol = temp_subvols.create('test_clone_special_files_dest')

            src_subvol.run_as_root(['mkfifo', src_subvol.path('fifo')])
            src_subvol.run_as_root([
                'mknod',
                src_subvol.path('null'),
                'c',
                '1',
                '3',
            ])

            for name in ['fifo', 'null']:
                ci = self._clone_item(name, name, subvol=src_subvol)
                self.assertEqual(name, ci.dest)
                self._check_item(
                    ci,
                    {ProvidesFile(path=name)},
                    {require_directory('/')},
                )
                ci.build(dest_subvol, DUMMY_LAYER_OPTS)

            src_r = render_subvol(src_subvol)
            dest_r = render_subvol(dest_subvol)
            self.assertEqual(src_r, dest_r)
            self.assertEqual(
                ['(Dir)', {
                    'fifo': ['(FIFO)'],
                    'null': ['(Char 103)'],
                }], dest_r)
Exemplo n.º 5
0
 def test_tarball_generator(self):
     with temp_filesystem() as fs_path, tempfile.NamedTemporaryFile() as t, \
             ExitStack() as exit_stack:
         with tarfile.TarFile(t.name, 'w') as tar_obj:
             tar_obj.add(fs_path, filter=_tarinfo_strip_dir_prefix(fs_path))
         self._check_item(
             image_source_item(
                 TarballItem,
                 exit_stack=exit_stack,
                 layer_opts=DUMMY_LAYER_OPTS,
             )(
                 from_target='t',
                 into_dir='y',
                 source={
                     'generator':
                     '/bin/bash',
                     'generator_args': [
                         '-c',
                         'cp "$1" "$2"; basename "$1"',
                         'test_tarball_generator',  # $0
                         t.name,  # $1, making $2 the output directory
                     ],
                     'content_hash':
                     'sha256:' + _hash_path(t.name, 'sha256'),
                 },
                 force_root_ownership=False,
             ),
             temp_filesystem_provides('y'),
             {require_directory('y')},
         )
Exemplo n.º 6
0
 def test_clone_nonexistent_source(self):
     ci = self._clone_item('/no_such_path', '/none_such')
     self.assertEqual('none_such', ci.dest)
     with self.assertRaises(subprocess.CalledProcessError):
         self._check_item(ci, set(), {require_directory('/')})
     with TempSubvolumes(sys.argv[0]) as temp_subvols:
         subvol = temp_subvols.create('test_clone_nonexistent_source')
         with self.assertRaises(subprocess.CalledProcessError):
             ci.build(subvol, DUMMY_LAYER_OPTS)
Exemplo n.º 7
0
    def test_install_file(self):
        with tempfile.NamedTemporaryFile() as tf:
            os.chmod(tf.name, stat.S_IXUSR)
            exe_item = _install_file_item(
                from_target='t',
                source={'source': tf.name},
                dest='d/c',
            )
        ep = _InstallablePath(Path(tf.name), ProvidesFile(path='d/c'), 'a+rx')
        self.assertEqual((ep, ), exe_item.paths)
        self.assertEqual(tf.name.encode(), exe_item.source)
        self._check_item(exe_item, {ep.provides}, {require_directory('d')})

        # Checks `image.source(path=...)`
        with temp_dir() as td:
            os.mkdir(td / 'b')
            open(td / 'b/q', 'w').close()
            data_item = _install_file_item(
                from_target='t',
                source={
                    'source': td,
                    'path': '/b/q'
                },
                dest='d',
            )
        dp = _InstallablePath(td / 'b/q', ProvidesFile(path='d'), 'a+r')
        self.assertEqual((dp, ), data_item.paths)
        self.assertEqual(td / 'b/q', data_item.source)
        self._check_item(data_item, {dp.provides}, {require_directory('/')})

        # NB: We don't need to get coverage for this check on ALL the items
        # because the presence of the ProvidesDoNotAccess items it the real
        # safeguard -- e.g. that's what prevents TarballItem from writing
        # to /meta/ or other protected paths.
        with self.assertRaisesRegex(AssertionError, 'cannot start with meta/'):
            _install_file_item(
                from_target='t',
                source={'source': 'a/b/c'},
                dest='/meta/foo',
            )
Exemplo n.º 8
0
 def test_stat_options(self):
     self._check_item(
         MakeDirsItem(
             from_target='t',
             into_dir='x',
             path_to_make='y/z',
             mode=0o733,
             user_group='cat:dog',
         ),
         {ProvidesDirectory(path='x/y'),
          ProvidesDirectory(path='x/y/z')},
         {require_directory('x')},
     )
Exemplo n.º 9
0
 def test_clone_file(self):
     ci = self._clone_item('/rpm_test/hello_world.tar', '/cloned_hello.tar')
     self.assertEqual('cloned_hello.tar', ci.dest)
     self._check_item(
         ci,
         {ProvidesFile(path='cloned_hello.tar')},
         {require_directory('/')},
     )
     with TempSubvolumes(sys.argv[0]) as temp_subvols:
         subvol = temp_subvols.create('test_clone_file')
         ci.build(subvol, DUMMY_LAYER_OPTS)
         r = render_subvol(subvol)
         ino, = pop_path(r, 'cloned_hello.tar')
         self.assertRegex(ino, '(File m444 d[0-9]+)')
         self.assertEqual(['(Dir)', {}], r)
Exemplo n.º 10
0
 def test_clone_pre_existing_dest(self):
     ci = self._clone_item('/foo/bar', '/', pre_existing_dest=True)
     self.assertEqual('', ci.dest)
     self._check_item(
         ci,
         {
             ProvidesDirectory(path='bar'),
             ProvidesDirectory(path='bar/baz'),
             ProvidesFile(path='bar/baz/bar'),
             ProvidesFile(path='bar/even_more_hello_world.tar'),
         },
         {require_directory('/')},
     )
     with TempSubvolumes(sys.argv[0]) as temp_subvols:
         subvol = temp_subvols.create('test_clone_pre_existing_dest')
         self._check_clone_bar(ci, subvol)
Exemplo n.º 11
0
    def test_clone_hardlinks(self):
        with TempSubvolumes(sys.argv[0]) as temp_subvols:
            src_subvol = temp_subvols.create('test_clone_hardlinks_src')
            dest_subvol = temp_subvols.create('test_clone_hardlinks_dest')

            src_subvol.run_as_root(['touch', src_subvol.path('a')])
            src_subvol.run_as_root([
                'ln',
                src_subvol.path('a'),
                src_subvol.path('b'),
            ])

            ci = self._clone_item(
                '/',
                '/',
                omit_outer_dir=True,
                pre_existing_dest=True,
                subvol=src_subvol,
            )
            self.assertEqual('', ci.dest)
            self._check_item(
                ci,
                {
                    ProvidesFile(path='a'),
                    ProvidesFile(path='b'),
                    # This looks like a bug (there's no /meta on disk here) but
                    # it's really just an artifact of how this path is
                    # protected.  Read: This Is Fine (TM).
                    ProvidesDoNotAccess(path='/meta'),
                },
                {require_directory('/')})
            ci.build(dest_subvol, DUMMY_LAYER_OPTS)

            src_r = render_subvol(src_subvol)
            dest_r = render_subvol(dest_subvol)
            self.assertEqual(src_r, dest_r)
            self.assertEqual(
                [
                    '(Dir)',
                    {
                        # Witness that they have the same (rendered) inode # of "0"
                        'a': [['(File)', 0]],
                        'b': [['(File)', 0]],
                    }
                ],
                dest_r)
Exemplo n.º 12
0
 def test_install_file_from_layer(self):
     layer = find_built_subvol(
         Path(__file__).dirname() / 'test-with-one-local-rpm')
     path_in_layer = b'rpm_test/cheese2.txt'
     item = _install_file_item(
         from_target='t',
         source={
             'layer': layer,
             'path': '/' + path_in_layer.decode()
         },
         dest='cheese2',
     )
     source_path = layer.path(path_in_layer)
     p = _InstallablePath(source_path, ProvidesFile(path='cheese2'), 'a+r')
     self.assertEqual((p, ), item.paths)
     self.assertEqual(source_path, item.source)
     self._check_item(item, {p.provides}, {require_directory('/')})
Exemplo n.º 13
0
 def test_clone_demo_sendstream(self):
     src_subvol = layer_resource_subvol(__package__, 'create_ops')
     ci = self._clone_item(
         '/',
         '/',
         omit_outer_dir=True,
         pre_existing_dest=True,
         subvol=src_subvol,
     )
     self.assertEqual({require_directory('/')}, set(ci.requires()))
     self.assertGreater(len(set(ci.provides())), 1)
     with TempSubvolumes(sys.argv[0]) as temp_subvols:
         dest_subvol = temp_subvols.create('create_ops')
         ci.build(dest_subvol, DUMMY_LAYER_OPTS)
         self.assertEqual(
             render_subvol(src_subvol),
             render_subvol(dest_subvol),
         )
Exemplo n.º 14
0
 def test_clone_omit_outer_dir(self):
     ci = self._clone_item(
         '/foo/bar',
         '/bar',
         omit_outer_dir=True,
         pre_existing_dest=True,
     )
     self.assertEqual('bar', ci.dest)
     self._check_item(
         ci,
         {
             ProvidesDirectory(path='bar/baz'),
             ProvidesFile(path='bar/baz/bar'),
             ProvidesFile(path='bar/even_more_hello_world.tar'),
         },
         {require_directory('/bar')},
     )
     with TempSubvolumes(sys.argv[0]) as temp_subvols:
         subvol = temp_subvols.create('test_clone_omit_outer_dir')
         subvol.run_as_root(['mkdir', subvol.path('bar')])
         self._check_clone_bar(ci, subvol)
Exemplo n.º 15
0
    def test_install_file_command_recursive(self):
        with TempSubvolumes(sys.argv[0]) as temp_subvolumes:
            subvol = temp_subvolumes.create('tar-sv')
            subvol.run_as_root(['mkdir', subvol.path('d')])

            with temp_dir() as td:
                with open(td / 'data.txt', 'w') as df:
                    print('Hello', file=df)
                os.mkdir(td / 'subdir')
                with open(td / 'subdir/exe.sh', 'w') as ef:
                    print('#!/bin/sh\necho "Hello"', file=ef)
                os.chmod(td / 'subdir/exe.sh', 0o100)

                dir_item = _install_file_item(
                    from_target='t',
                    source={'source': td},
                    dest='/d/a',
                )

                ps = [
                    _InstallablePath(
                        td,
                        ProvidesDirectory(path='d/a'),
                        'u+rwx,og+rx',
                    ),
                    _InstallablePath(
                        td / 'data.txt',
                        ProvidesFile(path='d/a/data.txt'),
                        'a+r',
                    ),
                    _InstallablePath(
                        td / 'subdir',
                        ProvidesDirectory(path='d/a/subdir'),
                        'u+rwx,og+rx',
                    ),
                    _InstallablePath(
                        td / 'subdir/exe.sh',
                        ProvidesFile(path='d/a/subdir/exe.sh'),
                        'a+rx',
                    ),
                ]
                self.assertEqual(sorted(ps), sorted(dir_item.paths))
                self.assertEqual(td, dir_item.source)
                self._check_item(dir_item, {p.provides
                                            for p in ps},
                                 {require_directory('d')})

                # This implicitly checks that `a` precedes its contents.
                dir_item.build(subvol, DUMMY_LAYER_OPTS)

            self.assertEqual(
                [
                    '(Dir)', {
                        'd': [
                            '(Dir)', {
                                'a': [
                                    '(Dir)', {
                                        'data.txt': ['(File m444 d6)'],
                                        'subdir': [
                                            '(Dir)', {
                                                'exe.sh': ['(File m555 d23)'],
                                            }
                                        ],
                                    }
                                ]
                            }
                        ]
                    }
                ],
                render_subvol(subvol),
            )
Exemplo n.º 16
0
 def requires(self):
     # We don't require the mountpoint itself since it will be shadowed,
     # so this item just makes it with default permissions.
     yield require_directory(os.path.dirname(self.mountpoint))
Exemplo n.º 17
0
 def requires(self):
     if not _whitelisted_symlink_source(self.source):
         yield require_file(self.source)
     yield require_directory(os.path.dirname(self.dest))
Exemplo n.º 18
0
    def test_mount_item(self):
        with TempSubvolumes(sys.argv[0]) as temp_subvolumes, \
                tempfile.TemporaryDirectory() as source_dir:
            runtime_source = {'so': 'me', 'arbitrary': {'j': 'son'}}
            mount_config = {
                'is_directory': True,
                'build_source': {
                    'type': 'layer',
                    'source': '//fake:path'
                },
                'runtime_source': runtime_source,
            }
            with open(os.path.join(source_dir, 'mountconfig.json'), 'w') as f:
                json.dump(mount_config, f)
            self._check_item(
                self._make_mount_item(
                    mountpoint='can/haz',
                    target=source_dir,
                    mount_config=mount_config,
                ),
                {ProvidesDoNotAccess(path='can/haz')},
                {require_directory('can')},
            )

            # Make a subvolume that we will mount inside `mounter`
            mountee = temp_subvolumes.create('moun:tee/volume')
            mountee.run_as_root(['tee', mountee.path('kitteh')],
                                input=b'cheez')

            # These sub-mounts inside `mountee` act as canaries to make sure
            # that (a) `mounter` receives the sub-mounts as a consequence of
            # mounting `mountee` recursively, (b) that unmounting one in
            # `mounter` does not affect the original in `mountee` -- i.e.
            # that rslave propagation is set up correctly, (c) that
            # unmounting in `mountee` immediately affects `mounter`.
            #
            # In practice, our build artifacts should NEVER be mutated after
            # construction (and the only un-mount is implicitly, and
            # seemingly safely, performed by `btrfs subvolume delete`).
            # However, ensuring that we have correct `rslave` propagation is
            # a worthwhile safeguard for host mounts, where an errant
            # `umount` by a user inside their repo could otherwise break
            # their host.
            for submount in ('submount1', 'submount2'):
                mountee.run_as_root(['mkdir', mountee.path(submount)])
                mountee.run_as_root([
                    'mount', '-o', 'bind,ro', source_dir,
                    mountee.path(submount)
                ])
                self.assertTrue(
                    os.path.exists(mountee.path(submount +
                                                '/mountconfig.json')))

            # Make the JSON file normally in "buck-out" that refers to `mountee`
            mountee_subvolumes_dir = self._write_layer_json_into(
                mountee, source_dir)

            # Mount <mountee> at <mounter>/meow
            mounter = temp_subvolumes.caller_will_create('mount:er/volume')
            root_item = FilesystemRootItem(from_target='t')
            root_item.get_phase_builder([root_item], DUMMY_LAYER_OPTS)(mounter)
            mount_meow = self._make_mount_item(
                mountpoint='meow',
                target=source_dir,
                mount_config=mount_config,
            )
            self.assertEqual(
                runtime_source,
                json.loads(mount_meow.runtime_source),
            )
            with self.assertRaisesRegex(AssertionError, ' could not resolve '):
                mount_meow.build_source.to_path(
                    target_to_path={},
                    subvolumes_dir=mountee_subvolumes_dir,
                )
            mount_meow.build(
                mounter,
                DUMMY_LAYER_OPTS._replace(
                    target_to_path={'//fake:path': source_dir},
                    subvolumes_dir=mountee_subvolumes_dir,
                ))

            # This checks the subvolume **contents**, but not the mounts.
            # Ensure the build created a mountpoint, and populated metadata.
            self._check_subvol_mounts_meow(mounter)

            # `mountee` was also mounted at `/meow`
            with open(mounter.path('meow/kitteh')) as f:
                self.assertEqual('cheez', f.read())

            def check_mountee_mounter_submounts(submount_presence):
                for submount, (in_mountee, in_mounter) in submount_presence:
                    self.assertEqual(
                        in_mountee,
                        os.path.exists(
                            mountee.path(submount + '/mountconfig.json')),
                        f'{submount}, {in_mountee}')
                    self.assertEqual(
                        in_mounter,
                        os.path.exists(
                            mounter.path('meow/' + submount +
                                         '/mountconfig.json')),
                        f'{submount}, {in_mounter}')

            # Both sub-mounts are accessible in both places now.
            check_mountee_mounter_submounts([
                ('submount1', (True, True)),
                ('submount2', (True, True)),
            ])
            # Unmounting `submount1` from `mountee` also affects `mounter`.
            mountee.run_as_root(['umount', mountee.path('submount1')])
            check_mountee_mounter_submounts([
                ('submount1', (False, False)),
                ('submount2', (True, True)),
            ])
            # Unmounting `submount2` from `mounter` doesn't affect `mountee`.
            mounter.run_as_root(['umount', mounter.path('meow/submount2')])
            check_mountee_mounter_submounts([
                ('submount1', (False, False)),
                ('submount2', (True, False)),
            ])

            # Check that we read back the `mounter` metadata, mark `/meow`
            # inaccessible, and do not emit a `ProvidesFile` for `kitteh`.
            self._check_item(
                PhasesProvideItem(from_target='t', subvol=mounter),
                {
                    ProvidesDirectory(path='/'),
                    ProvidesDoNotAccess(path='/meta'),
                    ProvidesDoNotAccess(path='/meow'),
                },
                set(),
            )
            # Check that we successfully clone mounts from the parent layer.
            mounter_child = temp_subvolumes.caller_will_create('child/volume')
            ParentLayerItem.get_phase_builder(
                [ParentLayerItem(from_target='t', subvol=mounter)],
                DUMMY_LAYER_OPTS,
            )(mounter_child)

            # The child has the same mount, and the same metadata
            self._check_subvol_mounts_meow(mounter_child)

            # Check that we refuse to create nested mounts.
            nested_mounter = temp_subvolumes.create('nested_mounter')
            nested_item = MountItem(
                layer_opts=DUMMY_LAYER_OPTS,
                from_target='t',
                mountpoint='/whatever',
                target=None,
                mount_config={
                    'is_directory': True,
                    'build_source': {
                        'type': 'layer',
                        'source': '//:fake'
                    },
                },
            )
            with tempfile.TemporaryDirectory() as d:
                mounter_subvolumes_dir = self._write_layer_json_into(
                    mounter, d)
                with self.assertRaisesRegex(
                        AssertionError,
                        'Refusing .* nested mount',
                ):
                    nested_item.build(
                        nested_mounter,
                        DUMMY_LAYER_OPTS._replace(
                            target_to_path={'//:fake': d},
                            subvolumes_dir=mounter_subvolumes_dir,
                        ))
Exemplo n.º 19
0
 def requires(self):
     yield require_directory(self.into_dir)
Exemplo n.º 20
0
 def requires(self):
     yield require_directory(self.dest if self.pre_existing_dest else os.
                             path.dirname(self.dest))
Exemplo n.º 21
0
 def requires(self):
     yield require_directory(self.source)
     yield require_directory(os.path.dirname(self.dest))