def to_path( self, *, target_to_path: Mapping[str, str], subvolumes_dir: str, ) -> str: if self.type == 'layer': out_path = target_to_path.get(self.source) if out_path is None: raise AssertionError( f'MountItem could not resolve {self.source}' ) subvol = find_built_subvol(out_path, subvolumes_dir=subvolumes_dir) # If we allowed mounting a layer that has other mounts inside, # it would force us to support nested mounts. We don't want to # do this (yet). if os.path.exists(subvol.path(META_MOUNTS_DIR)): raise AssertionError( f'Refusing to mount {subvol.path()} since that would ' 'require the tooling to support nested mounts.' ) return subvol.path() elif self.type == 'host': return self.source else: # pragma: no cover raise AssertionError( f'Bad mount source "{self.type}" for {self.source}' )
def replace_targets_by_paths(x, layer_opts: LayerOpts): ''' Converts target_tagger.bzl sigils to buck-out paths or Subvol objects. JSON-serialized image features store single-item dicts of the form {'__BUCK{_LAYER,}_TARGET': '//target:path'} whenever the compiler requires a path to another target. This is because actual paths would break Buck caching, and would not survive repo moves. Then, at runtime, the compiler receives a dictionary of target-to-path mappings as `--child-dependencies`, and performs the substitution in any image feature JSON it consumes. ''' if type(x) is dict: if '__BUCK_TARGET' in x or '__BUCK_LAYER_TARGET' in x: assert len(x) == 1, x (sigil, target), = x.items() path = layer_opts.target_to_path.get(target) if not path: raise RuntimeError( f'{target} not in {layer_opts.target_to_path}' ) return path if sigil == '__BUCK_TARGET' else find_built_subvol( path, subvolumes_dir=layer_opts.subvolumes_dir, ) return { k: replace_targets_by_paths(v, layer_opts) for k, v in x.items() } elif type(x) is list: return [replace_targets_by_paths(v, layer_opts) for v in x] elif type(x) in [int, float, str, bool, type(None)]: return x raise AssertionError(f'Unknown {type(x)} for {x}') # pragma: no cover
def _temp_resource_subvol(self, name: str): parent_sv = find_built_subvol(load_location(__package__, name)) with TempSubvolumes(sys.argv[0]) as temp_subvols: # Cannot use `.snapshot()` since that doesn't handle mounts. child_sv = temp_subvols.caller_will_create(name) ParentLayerItem.get_phase_builder([ ParentLayerItem(from_target='t', subvol=parent_sv), ], DUMMY_LAYER_OPTS)(child_sv) yield child_sv
def test_check_layers(self): meta = {'meta': ['(Dir)', {'private': ['(Dir)', {'opts': ['(Dir)', { 'artifacts_may_require_repo': ['(File d2)'], }]}]}]} # The parent has a couple of directories. self.assertEqual( ['(Dir)', {'a': ['(Dir)', {'b': ['(Dir)', {}]}], **meta}], render_sendstream(find_built_subvol( self._resource_path('parent') ).mark_readonly_and_get_sendstream()), ) # The child is near-empty because the `remove_paths` cleaned it up. self.assertEqual( ['(Dir)', {**meta}], render_sendstream(find_built_subvol( self._resource_path('child') ).mark_readonly_and_get_sendstream()), )
def test_contents(self): self.maxDiff = None sv = find_built_subvol(load_location(__package__, 'install-toy-rpm')) r = render_sendstream(sv.mark_readonly_and_get_sendstream()) self.assertEqual([ '(Dir)', {'bin': ['(Dir)', {'toy_src_file': ['(File m755 d40)']}]} ], pop_path(r, 'usr')) check_common_rpm_render(self, r, 'yum')
def test_rpm_metadata_from_subvol(self): layer_path = os.path.join(os.path.dirname(__file__), 'child-layer') child_subvol = find_built_subvol(layer_path) a = RpmMetadata.from_subvol(child_subvol, 'rpm-test-mice') self.assertEqual(a.name, 'rpm-test-mice') self.assertEqual(a.epoch, 0) self.assertEqual(a.version, '0.1') self.assertEqual(a.release, 'a') # not installed with self.assertRaises(RuntimeError): a = RpmMetadata.from_subvol(child_subvol, 'rpm-test-carrot') # subvol with no RPM DB layer_path = os.path.join(os.path.dirname(__file__), 'hello-layer') hello_subvol = find_built_subvol(layer_path) with self.assertRaisesRegex(ValueError, ' does not exist$'): a = RpmMetadata.from_subvol(hello_subvol, 'rpm-test-mice')
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('/')})
def test_clone_demo_sendstream(self): src_subvol = find_built_subvol(load_location(__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), )
def _subvol_from_resource(cls, module, name): return find_built_subvol(load_location(module, name))
def test_find_built_subvol(self): with open(find_built_subvol( # This works in @mode/opt since this artifact is baked into the XAR os.path.join(os.path.dirname(__file__), 'hello_world_base'), ).path('hello_world')) as f: self.assertEqual('', f.read())
def layer_resource_subvol(package: AnyStr, name: AnyStr) -> Subvol: 'Docs on the `layer_resource` Buck macro.' return find_built_subvol(importlib.resources.read_text(package, name))
def get_dummy_layer_opts_ba(): return DUMMY_LAYER_OPTS._replace( build_appliance=find_built_subvol(load_location( __package__, 'host-test-build-appliance', )) )
def _build_appliance() -> Subvol: return find_built_subvol(load_location(__package__, 'build-appliance'))
def setUpClass(cls): cls.src_layer = find_built_subvol( load_location(__package__, 'src-layer'))