def test_prober_jumbo_pallets(self, test_file): ''' test that find_pallets can identify jumbo pallets ''' paldir = tempfile.TemporaryDirectory() *pallet_data, expected_probe, filemap = PALLET_DATA[0] for input_file, dest in filemap.items(): pathlib.Path(f'{paldir.name}/pallet1/{dest}').parent.mkdir( parents=True, exist_ok=True) shutil.copyfile(test_file(f'pallet_artifacts/{input_file}'), f'{paldir.name}/pallet1/{dest}') p1 = pal.PalletInfo(*pallet_data, f'{paldir.name}/pallet1', expected_probe) *pallet_data, expected_probe, filemap = PALLET_DATA[1] for input_file, dest in filemap.items(): pathlib.Path(f'{paldir.name}/pallet2/{dest}').parent.mkdir( parents=True, exist_ok=True) shutil.copyfile(test_file(f'pallet_artifacts/{input_file}'), f'{paldir.name}/pallet2/{dest}') p2 = pal.PalletInfo(*pallet_data, f'{paldir.name}/pallet2', expected_probe) prober = pal.Prober() pal_map = prober.find_pallets(paldir.name) assert sorted(*pal_map.values()) == sorted([p1, p2])
def test_prober_multi_args(self, test_file): ''' test that find_pallets can accept multiple paths and return multiple pallets ''' paldirs = [] palinfos = [] *pallet_data, expected_probe, filemap = PALLET_DATA[0] paldir = tempfile.TemporaryDirectory() for input_file, dest in filemap.items(): pathlib.Path(f'{paldir.name}/{dest}').parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(test_file(f'pallet_artifacts/{input_file}'), f'{paldir.name}/{dest}') paldirs.append(paldir.name) palinfos.append( [pal.PalletInfo(*pallet_data, paldir.name, expected_probe)]) *pallet_data, expected_probe, filemap = PALLET_DATA[1] paldir2 = tempfile.TemporaryDirectory() for input_file, dest in filemap.items(): pathlib.Path(f'{paldir2.name}/{dest}').parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(test_file(f'pallet_artifacts/{input_file}'), f'{paldir2.name}/{dest}') paldirs.append(paldir2.name) palinfos.append( [pal.PalletInfo(*pallet_data, paldir2.name, expected_probe)]) prober = pal.Prober() pal_map = prober.find_pallets(*paldirs) assert sorted(pal_map.values()) == sorted(palinfos)
def test_prober(self, test_file, name, version, release, arch, distro_family, expected_probe, filemap): ''' test that the sample PALLET_DATA is identified correctly. ''' prober = pal.Prober() with tempfile.TemporaryDirectory() as paldir: for input_file, dest in filemap.items(): pathlib.Path(f'{paldir}/{dest}').parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(test_file(f'pallet_artifacts/{input_file}'), f'{paldir}/{dest}') pal_map = prober.find_pallets(paldir) assert pal_map[paldir] == [ pal.PalletInfo(name, version, release, arch, distro_family, paldir, expected_probe) ]
def test_find_no_pallets(self): with tempfile.TemporaryDirectory() as paldir: prober = pal.Prober() pal_map = prober.find_pallets(paldir) assert pal_map[paldir] == []
def test_collect_probes(self): ''' test that instantiating Prober finds at least the probes that stacki ships ''' prober = pal.Prober() assert len(prober.probes) >= 5
def run(self, params, args): clean, stacki_pallet_dir, updatedb, self.username, self.password = self.fillParams( [ ('clean', False), ('dir', '/export/stack/pallets'), ('updatedb', True), ('username', None), ('password', None), ]) # need to provide either both or none if self.username or self.password and not all( (self.username, self.password)): raise UsageError(self, 'must supply a password along with the username') clean = self.str2bool(clean) updatedb = self.str2bool(updatedb) # create a contextmanager that we can append cleanup jobs to # add its closing to run atexit, so we know it will run self.deferred = ExitStack() atexit.register(self.deferred.close) # special case: no args were specified - check if a pallet is mounted at /mnt/cdrom if not args: mount_point = '/mnt/cdrom' result = self._exec(f'mount | grep {mount_point}', shell=True) if result.returncode != 0: raise CommandError( self, 'no pallets specified and /mnt/cdrom is unmounted') args.append(mount_point) # resolve args and check for existence bad_args = [] for i, arg in enumerate(list(args)): # TODO: is this a problem? if arg.startswith(('https://', 'http://', 'ftp://')): args[i] = arg continue p = pathlib.Path(arg) if not p.exists(): bad_args.append(arg) else: args[i] = str(p.resolve()) if bad_args: msg = 'The following arguments appear to be local paths that do not exist: ' raise CommandError(self, msg + ', '.join(bad_args)) # most plugins will need a temporary directory, so allocate them here so we do cleanup # 'canonical_arg' is the arg provided by the user, but cleaned to be explicit (relative # paths resolved, etc) # 'exploded_path' is the directory where we will start searching for pallets # 'matched_pallets' is a list of pallet_info objects found at that path. pallet_args = {} for arg in args: tmpdir = tempfile.mkdtemp() self.deferred.callback(shutil.rmtree, tmpdir) pallet_args[arg] = { 'canonical_arg': arg, 'exploded_path': tmpdir, 'matched_pallets': [], } self.runPlugins(pallet_args) prober = probepal.Prober() pallet_infos = prober.find_pallets( *[pallet_args[path]['exploded_path'] for path in pallet_args]) # pallet_infos returns a dict {path: [pallet1, ...]} # note the list - an exploded_path can point to a jumbo pallet for path, pals in pallet_infos.items(): for arg in pallet_args: if pallet_args[arg]['exploded_path'] == path: pallet_args[arg]['matched_pallets'] = pals # TODO what to do if we match something twice. bad_args = [ arg for arg, info in pallet_args.items() if not info['matched_pallets'] ] if bad_args: msg = 'The following arguments do not appear to be pallets: ' raise CommandError(self, msg + ', '.join(bad_args)) # work off of a copy of pallet args, as we modify it as we go for arg, data in pallet_args.copy().items(): if len(data['matched_pallets']) == 1: pallet_args[arg]['exploded_path'] = data['matched_pallets'][ 0].pallet_root continue # delete the arg pointing to a jumbo and replace it with N new 'dummy' args del pallet_args[arg] for pal in data['matched_pallets']: fake_arg_name = '-'.join(info_getter(pal)) pallet_args[fake_arg_name] = data.copy() pallet_args[fake_arg_name]['exploded_path'] = pal.pallet_root pallet_args[fake_arg_name]['matched_pallets'] = [pal] # we want to be able to go tempdir to arg # this is because we want `canonical_arg` to be what goes in as the `URL` field in the db paths_to_args = { data['exploded_path']: data['canonical_arg'] for data in pallet_args.values() } # we have everything we need, copy the pallet to the fs, add it to the db, and maybe patch it for pallet in flatten(pallet_infos.values()): self.copy(stacki_pallet_dir, pallet, clean) self.write_pallet_xml(stacki_pallet_dir, pallet) if updatedb: self.update_db(pallet, paths_to_args[pallet.pallet_root]) if stacki_pallet_dir == '/export/stack/pallets': self.patch_pallet(pallet) # Clear the old packages self._exec('systemctl start ludicrous-cleaner'.split())