def test_all(self): """Publish the rpm rsync distributor before the yum distributor.""" cfg = config.get_config() if selectors.bug_is_untestable(2187, cfg.version): self.skipTest('https://pulp.plan.io/issues/2187') # Create a user and a repository. ssh_user, priv_key = self.make_user(cfg) ssh_identity_file = self.write_private_key(cfg, priv_key) repo = self.make_repo(cfg, {'remote': { 'host': urlparse(cfg.base_url).netloc, 'root': '/home/' + ssh_user, 'ssh_identity_file': ssh_identity_file, 'ssh_user': ssh_user, }}) # Publish with the rsync distributor. distribs = _get_dists_by_type_id(cfg, repo['_href']) self.verify_publish_is_skip(cfg, utils.publish_repo( cfg, repo, {'id': distribs['rpm_rsync_distributor']['id']} ).json()) # Verify that the rsync distributor hasn't placed files sudo = '' if utils.is_root(cfg) else 'sudo ' cmd = (sudo + 'ls -1 /home/{}'.format(ssh_user)).split() dirs = set(cli.Client(cfg).run(cmd).stdout.strip().split('\n')) self.assertNotIn('content', dirs)
def generate_repo_file(server_config, name, **kwargs): """Generate a repository file and returns its remote path. :param server_config: A :class:`pulp_smash.config.ServerConfig` object. :param name: file name and repo id (string inside []). :param kwargs: each item will be converted to repository properties where the key is the property name and the value its value. :returns: the remote path of the created repository file. """ repo = StringIO() repo.write('[{}]\n'.format(name)) path = os.path.join( '{}'.format('/etc/yum.repos.d/'), '{}.repo'.format(name)) if 'name' not in kwargs: repo.write('{}: {}\n'.format('name', name)) for key, value in kwargs.items(): repo.write('{}: {}\n'.format(key, value)) client = cli.Client(server_config) sudo = '' if is_root(server_config) else 'sudo ' client.machine.session().run( 'echo "{}" | {}tee {} > /dev/null'.format( repo.getvalue(), sudo, path ) ) repo.close() return path
def gen_yum_config_file(cfg, repositoryid, **kwargs): """Generate a yum configuration file and write it to ``/etc/yum.repos.d/``. Generate a yum configuration file containing a single repository section, and write it to ``/etc/yum.repos.d/{repositoryid}.repo``. :param pulp_smash.config.ServerConfig cfg: The system on which to create a yum configuration file. :param repositoryid: The section's ``repositoryid``. Used when naming the configuration file and populating the brackets at the head of the file. For details, see yum.conf(5). :param kwargs: Section options. Each kwarg corresponds to one option. For details, see yum.conf(5). :returns: The path to the yum configuration file. """ path = os.path.join('/etc/yum.repos.d/', repositoryid + '.repo') with StringIO() as section: section.write('[{}]\n'.format(repositoryid)) for key, value in kwargs.items(): section.write('{}: {}\n'.format(key, value)) cli.Client(cfg).machine.session().run( 'echo "{}" | {}tee {} > /dev/null' .format(section.getvalue(), '' if is_root(cfg) else 'sudo ', path) ) return path
def _set_pulp_manage_rsync(cfg, boolean): """Modify the ``pulp_manage_rsync`` SELinux policy. If the ``semanage`` executable is not available, return. Do this to deal with the possibility that SELinux is not installed on the system under test. For more information on the ``pulp_manage_rsync`` SELinux policy, see `ISO rsync Distributor → Configuration <http://docs.pulpproject.org/plugins/pulp_rpm/tech-reference/iso-rsync-distributor.html#configuration>`_. :param pulp_smash.config.ServerConfig cfg: Information about the system being modified. :param state: Either ``True`` or ``False``, indicating whether the ``pulp_manage_rsync`` SELinux policy should be turned on or off. :rtype: pulp_smash.cli.CompletedProcess """ policy = 'pulp_manage_rsync' client = cli.Client(cfg) try: client.run(('which', 'semanage')) except exceptions.CalledProcessError: return cmd = [] if utils.is_root(cfg) else ['sudo'] cmd.extend(['semanage', 'boolean', '--modify']) cmd.append('--on' if boolean else '--off') cmd.append(policy) return client.run(cmd)
def test_force_sync(self): """Test whether one can force Pulp to perform a full sync.""" cfg = config.get_config() if selectors.bug_is_untestable(1982, cfg.version): self.skipTest("https://pulp.plan.io/issues/1982") # Create and sync a repository. client = cli.Client(cfg) repo_id = utils.uuid4() client.run("pulp-admin rpm repo create --repo-id {} --feed {}".format(repo_id, RPM_SIGNED_FEED_URL).split()) self.addCleanup(client.run, "pulp-admin rpm repo delete --repo-id {}".format(repo_id).split()) sync_repo(cfg, repo_id) # Delete a random RPM rpms = self._list_rpms(cfg) client.run("{} rm -rf {}".format("sudo" if not is_root(cfg) else "", random.choice(rpms)).split()) with self.subTest(comment="Verify the RPM was removed."): self.assertEqual(len(self._list_rpms(cfg)), len(rpms) - 1) # Sync the repository *without* force_sync. sync_repo(cfg, repo_id) with self.subTest(comment="Verify the RPM has not been restored."): self.assertEqual(len(self._list_rpms(cfg)), len(rpms) - 1) # Sync the repository again sync_repo(cfg, repo_id, force_sync=True) with self.subTest(comment="Verify the RPM has been restored."): self.assertEqual(len(self._list_rpms(cfg)), len(rpms))
def verify_remote_units_path(self, cfg, distributor_cfg, num_units=None): """Verify the RPM rsync distributor has placed RPMs as appropriate. Verify that path ``{root}/{remote_units_path}/rpm/`` exists in the target system's filesystem, and that the correct number of RPMs are present in that directory. :param pulp_smash.config.PulpSmashConfig cfg: Information about the system onto which files have been published. :param distributor_cfg: A dict of information about an RPM rsync distributor. :param num_units: The number of units that should be on the target system's filesystem. Defaults to :data:`pulp_smash.constants.RPM_SIGNED_FEED_COUNT`. :returns: Nothing. """ if num_units is None: num_units = RPM_SIGNED_FEED_COUNT cli_client = cli.Client(cfg) sudo = () if utils.is_root(cfg) else ('sudo', ) path = distributor_cfg['config']['remote']['root'] remote_units_path = (distributor_cfg['config'].get( 'remote_units_path', 'content/units')) for segment in _split_path(remote_units_path): cmd = sudo + ('ls', '-1', path) files = set(cli_client.run(cmd).stdout.strip().split('\n')) self.assertIn(segment, files) path = os.path.join(path, segment) cmd = sudo + ('find', path, '-name', '*.rpm') files = cli_client.run(cmd).stdout.strip().split('\n') self.assertEqual(len(files), num_units, files)
def write_private_key(self, cfg, private_key): """Write the given private key to a file on disk. Ensure that the file is owned by user "apache" and has permissions of ``600``. In addition, schedule the key for deletion with ``self.addCleanup``. :param pulp_smash.config.ServerConfig cfg: Information about the server being targeted. :returns: The path to the private key on disk, as a string. """ sudo = '' if utils.is_root(cfg) else 'sudo ' client = cli.Client(cfg) ssh_identity_file = client.run(['mktemp']).stdout.strip() self.addCleanup(client.run, (sudo + 'rm ' + ssh_identity_file).split()) client.machine.session().run( "echo '{}' > {}".format(private_key, ssh_identity_file) ) client.run(['chmod', '600', ssh_identity_file]) client.run((sudo + 'chown apache ' + ssh_identity_file).split()) # Pulp's SELinux policy requires files handled by Pulp to have the # httpd_sys_rw_content_t label enforcing = client.run(['getenforce']).stdout.strip() if enforcing.lower() != 'disabled': client.run( (sudo + 'chcon -t httpd_sys_rw_content_t ' + ssh_identity_file) .split() ) return ssh_identity_file
def generate_content_source(server_config, name, **kwargs): """Generate a content source file and returns its remote path. See `Defining a Content Source`_ for more information. .. _Defining a Content Source: http://docs.pulpproject.org/user-guide/content-sources.html#defining-a-content-source :param server_config: A :class:`pulp_smash.config.ServerConfig` object. :param name: file name and content source id (string inside []). :param kwargs: each item will be converted to content source properties where the key is the property name and the value its value. :returns: the remote path of the created content source file. """ content_source = StringIO() content_source.write('[{}]\n'.format(name)) path = os.path.join( '{}'.format(CONTENT_SOURCES_PATH), '{}.conf'.format(name)) if 'name' not in kwargs: content_source.write('{}: {}\n'.format('name', name)) for key, value in kwargs.items(): content_source.write('{}: {}\n'.format(key, value)) client = cli.Client(server_config) sudo = '' if is_root(server_config) else 'sudo ' client.machine.session().run( 'echo "{}" | {}tee {} > /dev/null'.format( content_source.getvalue(), sudo, path ) ) content_source.close() return path
def gen_yum_config_file(cfg, repositoryid, **kwargs): """Generate a yum configuration file and write it to ``/etc/yum.repos.d/``. Generate a yum configuration file containing a single repository section, and write it to ``/etc/yum.repos.d/{repositoryid}.repo``. :param pulp_smash.config.PulpSmashConfig cfg: The system on which to create a yum configuration file. :param repositoryid: The section's ``repositoryid``. Used when naming the configuration file and populating the brackets at the head of the file. For details, see yum.conf(5). :param kwargs: Section options. Each kwarg corresponds to one option. For details, see yum.conf(5). :returns: The path to the yum configuration file. """ path = os.path.join('/etc/yum.repos.d/', repositoryid + '.repo') with StringIO() as section: section.write('[{}]\n'.format(repositoryid)) for key, value in kwargs.items(): section.write('{}: {}\n'.format(key, value)) cli.Client(cfg).machine.session().run( 'echo "{}" | {}tee {} > /dev/null'.format( section.getvalue(), '' if utils.is_root(cfg) else 'sudo ', path)) return path
def write_private_key(self, cfg, private_key): """Write the given private key to a file on disk. Ensure that the file is owned by user "apache" and has permissions of ``600``. In addition, schedule the key for deletion with ``self.addCleanup``. :param pulp_smash.config.PulpSmashConfig cfg: Information about the host being targeted. :returns: The path to the private key on disk, as a string. """ sudo = '' if utils.is_root(cfg) else 'sudo ' client = cli.Client(cfg) ssh_identity_file = client.run(['mktemp']).stdout.strip() self.addCleanup(client.run, (sudo + 'rm ' + ssh_identity_file).split()) client.machine.session().run("echo '{}' > {}".format( private_key, ssh_identity_file)) client.run(['chmod', '600', ssh_identity_file]) client.run((sudo + 'chown apache ' + ssh_identity_file).split()) # Pulp's SELinux policy requires files handled by Pulp to have the # httpd_sys_rw_content_t label enforcing = client.run(['getenforce']).stdout.strip() if enforcing.lower() != 'disabled': client.run((sudo + 'chcon -t httpd_sys_rw_content_t ' + ssh_identity_file).split()) return ssh_identity_file
def verify_remote_units_path(self, cfg, distributor_cfg): """Verify the RPM rsync distributor has placed RPMs as appropriate. Verify that path ``{root}/{remote_units_path}/rpm`` exists in the target system's filesystem, and that :data:`pulp_smash.constants.RPM_SIGNED_FEED_COUNT` RPMs are present in this directory. :param pulp_smash.config.ServerConfig cfg: Information about the system onto which files have been published. :param distributor_cfg: A dict of information about an RPM rsync distributor. :returns: Nothing. """ # This method avoids calling command_string.split() to avoid issues # with spaces and other funny character in path names. cli_client = cli.Client(cfg) sudo = () if utils.is_root(cfg) else ('sudo',) path = distributor_cfg['config']['remote']['root'] remote_units_path = distributor_cfg['config'].get( 'remote_units_path', 'content/units', ) for segment in _split_path(remote_units_path): cmd = sudo + ('ls', '-1', path) files = set(cli_client.run(cmd).stdout.strip().split('\n')) self.assertIn(segment, files) path = os.path.join(path, segment) cmd = sudo + ('find', path, '-name', '*.rpm') files = cli_client.run(cmd).stdout.strip().split('\n') self.assertEqual(len(files), RPM_SIGNED_FEED_COUNT, files)
def test_force_sync(self): """Test whether one can force Pulp to perform a full sync.""" cfg = config.get_config() if selectors.bug_is_untestable(1982, cfg.version): self.skipTest('https://pulp.plan.io/issues/1982') # Create and sync a repository. client = cli.Client(cfg) repo_id = utils.uuid4() client.run('pulp-admin rpm repo create --repo-id {} --feed {}'.format( repo_id, RPM_SIGNED_FEED_URL).split()) self.addCleanup( client.run, 'pulp-admin rpm repo delete --repo-id {}'.format(repo_id).split()) sync_repo(cfg, repo_id) # Delete a random RPM rpms = self._list_rpms(cfg) client.run('{} rm -rf {}'.format( 'sudo' if not is_root(cfg) else '', random.choice(rpms), ).split()) with self.subTest(comment='Verify the RPM was removed.'): self.assertEqual(len(self._list_rpms(cfg)), len(rpms) - 1) # Sync the repository *without* force_sync. sync_repo(cfg, repo_id) with self.subTest(comment='Verify the RPM has not been restored.'): self.assertEqual(len(self._list_rpms(cfg)), len(rpms) - 1) # Sync the repository again sync_repo(cfg, repo_id, force_sync=True) with self.subTest(comment='Verify the RPM has been restored.'): self.assertEqual(len(self._list_rpms(cfg)), len(rpms))
def set_pulp_manage_rsync(cfg, boolean): """Set the ``pulp_manage_rsync`` SELinux policy. If the ``semanage`` executable is not available, return. (This is the case if SELinux isn't installed on the system under test.) Otherwise, set the ``pulp_manage_rsync SELinux policy on or off, depending on the truthiness of ``boolean``. For more information on the ``pulp_manage_rsync`` SELinux policy, see `ISO rsync Distributor → Configuration <http://docs.pulpproject.org/plugins/pulp_rpm/tech-reference/iso-rsync-distributor.html#configuration>`_. :param pulp_smash.config.PulpSmashConfig cfg: Information about a Pulp host. :param boolean: Either ``True`` or ``False``. :returns: Information about the executed command, or ``None`` if no command was executed. :rtype: pulp_smash.cli.CompletedProcess """ sudo = () if utils.is_root(cfg) else ('sudo', ) client = cli.Client(cfg) try: # semanage is installed at /sbin/semanage on some distros, and requires # root privileges to discover. client.run(sudo + ('which', 'semanage')) except exceptions.CalledProcessError: return None cmd = sudo cmd += ('semanage', 'boolean', '--modify') cmd += ('--on', ) if boolean else ('--off', ) cmd += ('pulp_manage_rsync', ) return client.run(cmd)
def setUpClass(cls): """Create a content source.""" super(RefreshAndDeleteContentSourcesTestCase, cls).setUpClass() cls.cfg = config.get_config() if cls.cfg.version < Version('2.8.6'): raise unittest.SkipTest('This test requires at least 2.8.6') pulp_admin_login(cls.cfg) cls.client = cli.Client(cls.cfg) cls.content_source_id = uuid4() content_source_path = generate_content_source( cls.cfg, cls.content_source_id, enabled='1', type='yum', base_url=RPM_SIGNED_FEED_URL, ) sudo = '' if is_root(cls.cfg) else 'sudo ' cls.responses = [ cls.client.run( 'pulp-admin content sources refresh'.split() ), _get_content_source_ids(cls.cfg), cls.client.run( 'pulp-admin content sources refresh --source-id {}' .format(cls.content_source_id).split() ), ] cls.client.run( '{}rm -f {}'.format(sudo, content_source_path).split()) cls.responses.append(_get_content_source_ids(cls.cfg))
def verify_remote_units_path(self, cfg, distributor_cfg): """Verify the RPM rsync distributor has placed RPMs as appropriate. Verify that path ``{root}/{remote_units_path}/rpm`` exists in the target system's filesystem, and that :data:`pulp_smash.constants.RPM_SIGNED_FEED_COUNT` RPMs are present in this directory. :param pulp_smash.config.ServerConfig cfg: Information about the system onto which files have been published. :param distributor_cfg: A dict of information about an RPM rsync distributor. :returns: Nothing. """ # This method avoids calling command_string.split() to avoid issues # with spaces and other funny character in path names. cli_client = cli.Client(cfg) sudo = () if utils.is_root(cfg) else ('sudo', ) path = distributor_cfg['config']['remote']['root'] remote_units_path = distributor_cfg['config'].get( 'remote_units_path', 'content/units', ) for segment in _split_path(remote_units_path): cmd = sudo + ('ls', '-1', path) files = set(cli_client.run(cmd).stdout.strip().split('\n')) self.assertIn(segment, files) path = os.path.join(path, segment) cmd = sudo + ('find', path, '-name', '*.rpm') files = cli_client.run(cmd).stdout.strip().split('\n') self.assertEqual(len(files), RPM_SIGNED_FEED_COUNT, files)
def test_all(self): """Publish the rpm rsync distributor before the yum distributor.""" cfg = config.get_config() if selectors.bug_is_untestable(2187, cfg.version): self.skipTest('https://pulp.plan.io/issues/2187') # Create a user and a repository. ssh_user, priv_key = self.make_user(cfg) ssh_identity_file = self.write_private_key(cfg, priv_key) repo_href = self.make_repo( cfg, { 'remote': { 'host': urlparse(cfg.base_url).netloc, 'root': '/home/' + ssh_user, 'ssh_identity_file': ssh_identity_file, 'ssh_user': ssh_user, } }) # Publish with the rsync distributor. distribs = _get_dists_by_type_id(cfg, repo_href) self.verify_publish_is_skip( cfg, api.Client(cfg).post(urljoin(repo_href, 'actions/publish/'), { 'id': distribs['rpm_rsync_distributor']['id'] }).json()) # Verify that the rsync distributor hasn't placed files sudo = '' if utils.is_root(cfg) else 'sudo ' cmd = (sudo + 'ls -1 /home/{}'.format(ssh_user)).split() dirs = set(cli.Client(cfg).run(cmd).stdout.strip().split('\n')) self.assertNotIn('content', dirs)
def sudo(self): """Return either ``''`` or ``'sudo '``. Return the former if root, and the latter if not. """ if self.__sudo is None: self.__sudo = '' if utils.is_root(self.cfg) else 'sudo ' return self.__sudo
def setUpClass(cls): """Get all of the processes running on the target Pulp system.""" cfg = config.get_config() cmd = [] if utils.is_root(config.get_config()) else ['sudo'] cmd.extend(('ps', '-A', '-w', '-w', '-o', ','.join(PS_FIELDS))) cls.procs = [ Process(*line.split(maxsplit=1)) for line in cli.Client(cfg).run(cmd).stdout.splitlines() ]
def tearDown(self): """Reset the number of Pul pworkers, and reset Pulp. Reset Pulp because :meth:`test_all` may break Pulp. """ sudo = () if utils.is_root(self.cfg) else ('sudo', ) # Delete last line from file. cli.Client(self.cfg).run(sudo + ('sed', '-i', '$d', _PULP_WORKERS_CFG)) utils.reset_pulp(self.cfg)
def setUp(self): """Ensure there is only one Pulp worker.""" self.cfg = config.get_config() if selectors.bug_is_untestable(2835, self.cfg.pulp_version): self.skipTest('https://pulp.plan.io/issues/2835') sudo = '' if utils.is_root(self.cfg) else 'sudo' cli.Client(self.cfg).machine.session().run( "{} bash -c 'echo PULP_CONCURRENCY=1 >> {}'".format( sudo, _PULP_WORKERS_CFG)) cli.GlobalServiceManager(self.cfg).restart(PULP_SERVICES)
def test_dry_run(self): """Make sure pulp-manage-db runs if --dry-run is passed.""" if selectors.bug_is_untestable(2776, self.cfg.pulp_version): self.skipTest('https://pulp.plan.io/issues/2776') cmd = () if utils.is_root(self.cfg) else ('sudo',) cmd += ( 'runuser', '--shell', '/bin/sh', '--command', 'pulp-manage-db --dry-run', '-', 'apache' ) cli.Client(self.cfg).run(cmd)
def test_all(self): """Use the ``force_full`` RPM rsync distributor option.""" cfg = config.get_config() api_client = api.Client(cfg) cli_client = cli.Client(cfg) sudo = '' if utils.is_root(cfg) else 'sudo ' # Create a user and repo with an importer and distribs. Sync the repo. ssh_user, priv_key = self.make_user(cfg) ssh_identity_file = self.write_private_key(cfg, priv_key) repo_href = self.make_repo( cfg, { 'remote': { 'host': urlparse(cfg.base_url).netloc, 'root': '/home/' + ssh_user, 'ssh_identity_file': ssh_identity_file, 'ssh_user': ssh_user, } }) utils.sync_repo(cfg, repo_href) # Publish the repo with the yum and rsync distributors, respectively. # Verify that the RPM rsync distributor has placed files. distribs = _get_dists_by_type_id(cfg, repo_href) self.maybe_disable_selinux(cfg, 2199) for type_id in ('yum_distributor', 'rpm_rsync_distributor'): api_client.post(urljoin(repo_href, 'actions/publish/'), {'id': distribs[type_id]['id']}) self.verify_remote_units_path(cfg, distribs['rpm_rsync_distributor']) # Remove all files from the target directory, and publish again. Verify # that the RPM rsync distributor didn't place any files. cmd = sudo + 'rm -rf /home/{}/content'.format(ssh_user) cli_client.run(cmd.split()) self.verify_publish_is_skip( cfg, api_client.post(urljoin(repo_href, 'actions/publish/'), { 'id': distribs['rpm_rsync_distributor']['id'] }).json()) cmd = sudo + 'ls -1 /home/{}'.format(ssh_user) dirs = set(cli_client.run(cmd.split()).stdout.strip().split('\n')) self.assertNotIn('content', dirs) # Publish the repo with ``force_full`` set to true. Verify that the RPM # rsync distributor placed files. if selectors.bug_is_untestable(2202, cfg.version): return api_client.post( urljoin(repo_href, 'actions/publish/'), { 'id': distribs['rpm_rsync_distributor']['id'], 'override_config': { 'force_full': True }, }) self.verify_remote_units_path(cfg, distribs['rpm_rsync_distributor'])
def test_all(self): """Test whether copied files retain their original mtime. This test targets the following issues: * `Pulp #2783 <https://pulp.plan.io/issues/2783>`_ * `Pulp Smash #720 <https://github.com/PulpQE/pulp-smash/issues/720>`_ Do the following: 1. Create, sync and publish a repository, with ``generate_sqlite`` set to true. 2. Get the ``mtime`` of the sqlite files. 3. Upload an RPM package into the repository, and sync the repository. 4. Get the ``mtime`` of the sqlite files again. Verify that the mtimes are the same. """ cfg = config.get_config() if selectors.bug_is_untestable(2783, cfg.pulp_version): self.skipTest('https://pulp.plan.io/issues/2783') # Create, sync and publish a repository. client = api.Client(cfg, api.json_handler) body = gen_repo() body['importer_config']['feed'] = RPM_UNSIGNED_FEED_URL body['distributors'] = [gen_distributor()] body['distributors'][0]['distributor_config']['generate_sqlite'] = True repo = client.post(REPOSITORY_PATH, body) self.addCleanup(client.delete, repo['_href']) repo = client.get(repo['_href'], params={'details': True}) utils.sync_repo(cfg, repo) utils.publish_repo(cfg, repo) # Get the mtime of the sqlite files. cli_client = cli.Client(cfg, cli.echo_handler) cmd = '' if utils.is_root(cfg) else 'sudo ' cmd += "bash -c \"stat --format %Y '{}'/*\"".format( os.path.join( _PATH, repo['distributors'][0]['config']['relative_url'], 'repodata', )) mtimes_pre = ( cli_client.machine.session().run(cmd)[1].strip().split().sort()) # Upload to the repo, and sync it. rpm = utils.http_get(RPM_SIGNED_URL) utils.upload_import_unit(cfg, rpm, {'unit_type_id': 'rpm'}, repo) utils.sync_repo(cfg, repo) # Get the mtime of the sqlite files again. time.sleep(1) mtimes_post = ( cli_client.machine.session().run(cmd)[1].strip().split().sort()) self.assertEqual(mtimes_pre, mtimes_post)
def _verify_files_not_in_dir(self, cfg, distributor_cfg): """Verify no RPMs are in the distributor's ``remote_units_path``.""" path = os.path.join( distributor_cfg['config']['remote']['root'], distributor_cfg['config'].get('remote_units_path', 'content/units') ) cmd = ['find', path, '-name', '*.rpm'] if not utils.is_root(cfg): cmd.insert(0, 'sudo') files = cli.Client(cfg).run(cmd).stdout.strip().split('\n') self.assertEqual(len(files), 0, files)
def _verify_files_not_in_dir(self, cfg, *, yum_distributor, rpm_rsync_distributor): """Verify no RPMs are in the distributor's ``relative_url`` dir.""" path = os.path.join( rpm_rsync_distributor['config']['remote']['root'], yum_distributor['config']['relative_url'], ) cmd = ['find', path, '-name', '*.rpm'] if not utils.is_root(cfg): cmd.insert(0, 'sudo') files = cli.Client(cfg).run(cmd).stdout.strip().split('\n') self.assertEqual(files, ['']) # strange, but correct
def setUpClass(cls): """Maybe skip this test case.""" if inspect.getmro(cls)[0] == BaseTestCase: raise unittest.SkipTest('Abstract base class.') cls.cfg = config.get_config() if selectors.bug_is_untestable(2186, cls.cfg.pulp_version): raise unittest.SkipTest('https://pulp.plan.io/issues/2186') cls.cmd = () if utils.is_root(cls.cfg) else ('sudo',) cls.cmd += ( 'runuser', '--shell', '/bin/sh', '--command', 'pulp-manage-db', '-', 'apache' )
def test_all(self): """Test Pulp's handling of its ``PULP_MAX_TASKS_PER_CHILD`` setting.""" cfg = config.get_config() if selectors.bug_is_untestable(2172, cfg.version): self.skipTest('https://pulp.plan.io/issues/2172') set_opt = [ 'sed', '-i', '-e', 's/.*PULP_MAX_TASKS_PER_CHILD=[0-9]*$/PULP_MAX_TASKS_PER_CHILD=2/', '/etc/default/pulp_workers' ] reset_opt = [ 'sed', '-i', '-e', 's/^PULP_MAX_TASKS_PER_CHILD=2$/# PULP_MAX_TASKS_PER_CHILD=2/', '/etc/default/pulp_workers' ] if not utils.is_root(cfg): for cmd in (set_opt, reset_opt): cmd.insert(0, 'sudo') # Step 1 for proc in get_pulp_worker_procs(cfg): self.assertNotIn('--maxtasksperchild=2', proc) # Step 2 client = cli.Client(cfg) client.run(set_opt) self.addCleanup(client.run, reset_opt) restart_pulp(cfg) for proc in get_pulp_worker_procs(cfg): self.assertIn('--maxtasksperchild=2', proc) # Step 3 repo_id = utils.uuid4() proc = client.run( 'pulp-admin rpm repo create --repo-id {} --feed {}' .format(repo_id, RPM_UNSIGNED_FEED_URL).split() ) self.addCleanup( client.run, 'pulp-admin rpm repo delete --repo-id {}'.format(repo_id).split() ) self.assertNotIn('Task Failed', proc.stdout) proc = client.run( 'pulp-admin rpm repo sync run --repo-id {}'.format(repo_id).split() ) self.assertNotIn('Task Failed', proc.stdout) # Step 4 client.run(reset_opt) restart_pulp(cfg) for proc in get_pulp_worker_procs(cfg): self.assertNotIn('--maxtasksperchild=2', proc)
def test_all(self): """Check for content synced from a feed with PULP_DISTRIBUTION.xml.""" if self.cfg.pulp_version < version.Version('2.11.2'): self.skipTest( 'PULP_DISTRIBUTION.xml improved parsing is available on Pulp ' '2.11.2+') client = api.Client(self.cfg, api.json_handler) distributor = gen_distributor() distributor['auto_publish'] = True body = gen_repo() body['distributors'] = [distributor] body['importer_config'] = { 'feed': RPM_WITH_PULP_DISTRIBUTION_FEED_URL, } repo = client.post(REPOSITORY_PATH, body) self.addCleanup(client.delete, repo['_href']) utils.sync_repo(self.cfg, repo) repo = client.get(repo['_href'], params={'details': True}) self.assertEqual(repo['content_unit_counts']['distribution'], 1) cli_client = cli.Client(self.cfg, cli.code_handler) relative_url = repo['distributors'][0]['config']['relative_url'] sudo = () if utils.is_root(self.cfg) else ('sudo', ) pulp_distribution = cli_client.run(sudo + ( 'cat', os.path.join( '/var/lib/pulp/published/yum/http/repos/', relative_url, 'PULP_DISTRIBUTION.xml', ), )).stdout # make sure published repository PULP_DISTRIBUTION.xml does not include # any extra file from the original repo's PULP_DISTRIBUTION.xml under # metadata directory self.assertNotIn('metadata/productid', pulp_distribution) release_info = cli_client.run(sudo + ( 'cat', os.path.join( '/var/lib/pulp/published/yum/http/repos/', relative_url, 'release-notes/release-info', ), )).stdout response = requests.get( urljoin( urljoin(RPM_WITH_PULP_DISTRIBUTION_FEED_URL, 'release-notes/'), 'release-info', )) # make sure published repository has extra files outside the metadata # directory from the origiginal repo's PULP_DISTRIBUTION.xml self.assertEqual(release_info, response.text)
def setUpClass(cls): """Create an RPM repository and issue a task to download the repo. Do the following: 1. Reset Pulp. 2. Create a repository with the "on demand" download policy. 3. Sync and publish the repository. 4. Trigger a repository download. 5. Corrupt a file in the repository. 6. Trigger a repository download, without unit verification. 7. Trigger a repository download, with unit verification. """ super(FixFileCorruptionTestCase, cls).setUpClass() if (selectors.bug_is_untestable(1905, cls.cfg.version) and _os_is_rhel6(cls.cfg)): raise unittest.SkipTest('https://pulp.plan.io/issues/1905') # Ensure Pulp is empty of units otherwise we might just associate pre- # existing units. utils.reset_pulp(cls.cfg) # Create, sync and publish a repository. repo = _create_repo(cls.cfg, 'on_demand') cls.resources.add(repo['_href']) utils.sync_repo(cls.cfg, repo['_href']) # Trigger a repository download. Read the repo before and after. api_client = api.Client(cls.cfg, api.json_handler) download_path = urljoin(repo['_href'], 'actions/download/') params = {'details': True} cls.repo_pre_download = api_client.get(repo['_href'], params=params) api_client.post(download_path, {'verify_all_units': False}) cls.repo_post_download = api_client.get(repo['_href'], params=params) # Corrupt an RPM. The file is there, but the checksum isn't right. rpm_abs_path = cls.get_rpm_abs_path() cli_client = cli.Client(cls.cfg) sudo = '' if utils.is_root(cls.cfg) else 'sudo ' checksum_cmd = (sudo + 'sha256sum ' + rpm_abs_path).split() cls.sha_pre_corruption = cli_client.run(checksum_cmd).stdout.strip() cli_client.run((sudo + 'rm ' + rpm_abs_path).split()) cli_client.run((sudo + 'touch ' + rpm_abs_path).split()) cli_client.run((sudo + 'chown apache:apache ' + rpm_abs_path).split()) cls.sha_post_corruption = cli_client.run(checksum_cmd).stdout.strip() # Trigger repository downloads that don't and do checksum files, resp. api_client.post(download_path, {'verify_all_units': False}) cls.unverified_file_sha = cli_client.run(checksum_cmd).stdout.strip() api_client.post(download_path, {'verify_all_units': True}) cls.verified_file_sha = cli_client.run(checksum_cmd).stdout.strip()
def get_pulp_worker_procs(cfg): """Use ``ps aux`` to get information about each Pulp worker process. :param pulp_smash.config.ServerConfig cfg: Information about the Pulp server being targeted. :return: An iterable of strings, one per line of matching output. """ cmd = ['ps', 'aux'] if not utils.is_root(cfg): cmd.insert(0, 'sudo') return tuple(( proc for proc in cli.Client(cfg).run(cmd).stdout.splitlines() if 'celery worker' in proc and 'resource_manager' not in proc ))
def test_all(self): """Test whether one can force Pulp to perform a full sync.""" cfg = config.get_config() if selectors.bug_is_untestable(1982, cfg.pulp_version): self.skipTest('https://pulp.plan.io/issues/1982') # Create and sync a repository. client = cli.Client(cfg) repo_id = utils.uuid4() client.run(( 'pulp-admin', 'rpm', 'repo', 'create', '--repo-id', repo_id, '--feed', RPM_UNSIGNED_FEED_URL, )) self.addCleanup(client.run, ( 'pulp-admin', 'rpm', 'repo', 'delete', '--repo-id', repo_id, )) sync_repo(cfg, repo_id) # Delete a random RPM from the filesystem. rpms = self._list_rpms(cfg) rpm = random.choice(rpms) cmd = [] if not is_root(cfg): cmd.append('sudo') cmd.extend(('rm', '-rf', rpm)) client.run(cmd) with self.subTest(comment='verify the rpm has been removed'): self.assertEqual(len(self._list_rpms(cfg)), len(rpms) - 1, rpm) # Sync the repository without --force-full. sync_repo(cfg, repo_id) with self.subTest(comment='verify the rpm has not yet been restored'): self.assertEqual(len(self._list_rpms(cfg)), len(rpms) - 1, rpm) # Sync the repository with --force-full. sync_repo(cfg, repo_id, force_sync=True) with self.subTest(comment='verify the rpm has been restored'): self.assertEqual(len(self._list_rpms(cfg)), len(rpms), rpm)
def test_all(self): """Use the ``force_full`` RPM rsync distributor option.""" cfg = config.get_config() api_client = api.Client(cfg) cli_client = cli.Client(cfg) sudo = '' if utils.is_root(cfg) else 'sudo ' # Create a user and repo with an importer and distribs. Sync the repo. ssh_user, priv_key = self.make_user(cfg) ssh_identity_file = self.write_private_key(cfg, priv_key) repo_href = self.make_repo(cfg, {'remote': { 'host': urlparse(cfg.base_url).netloc, 'root': '/home/' + ssh_user, 'ssh_identity_file': ssh_identity_file, 'ssh_user': ssh_user, }}) utils.sync_repo(cfg, repo_href) # Publish the repo with the yum and rsync distributors, respectively. # Verify that the RPM rsync distributor has placed files. distribs = _get_dists_by_type_id(cfg, repo_href) self.maybe_disable_selinux(cfg, 2199) for type_id in ('yum_distributor', 'rpm_rsync_distributor'): api_client.post(urljoin(repo_href, 'actions/publish/'), { 'id': distribs[type_id]['id'] }) self.verify_files_in_dir(cfg, distribs['rpm_rsync_distributor']) # Remove all files from the target directory, and publish again. Verify # that the RPM rsync distributor didn't place any files. cmd = sudo + 'rm -rf /home/{}/content'.format(ssh_user) cli_client.run(cmd.split()) self.verify_publish_is_skip(cfg, api_client.post( urljoin(repo_href, 'actions/publish/'), {'id': distribs['rpm_rsync_distributor']['id']} ).json()) cmd = sudo + 'ls -1 /home/{}'.format(ssh_user) dirs = set(cli_client.run(cmd.split()).stdout.strip().split('\n')) self.assertNotIn('content', dirs) # Publish the repo with ``force_full`` set to true. Verify that the RPM # rsync distributor placed files. if selectors.bug_is_untestable(2202, cfg.version): return api_client.post(urljoin(repo_href, 'actions/publish/'), { 'id': distribs['rpm_rsync_distributor']['id'], 'override_config': {'force_full': True}, }) self.verify_files_in_dir(cfg, distribs['rpm_rsync_distributor'])
def _verify_files_not_in_dir( self, cfg, *, yum_distributor, rpm_rsync_distributor): """Verify no RPMs are in the distributor's ``relative_url`` dir.""" path = os.path.join( rpm_rsync_distributor['config']['remote']['root'], yum_distributor['config']['relative_url'], ) cmd = ['find', path, '-name', '*.rpm'] if not utils.is_root(cfg): cmd.insert(0, 'sudo') files = cli.Client(cfg).run(cmd).stdout.strip().split('\n') self.assertEqual(files, ['']) # strange, but correct
def make_user(self, cfg): """Create a user account with a home directory and an SSH keypair. In addition, schedule the user for deletion with ``self.addCleanup``. :param pulp_smash.config.ServerConfig cfg: Information about the server being targeted. :returns: A ``(username, private_key)`` tuple. """ sudo = '' if utils.is_root(cfg) else 'sudo ' creator = _make_user(cfg) username = next(creator) self.addCleanup( cli.Client(cfg).run, (sudo + 'userdel --remove {}').format(username).split()) private_key = next(creator) return (username, private_key)
def test_all(self): """Update an RPM in a repository and on a host.""" cfg = config.get_config() if check_issue_2277(cfg): raise unittest.SkipTest('https://pulp.plan.io/issues/2277') client = cli.Client(cfg) sudo = '' if is_root(cfg) else 'sudo ' # Create the second repository. repo_id = self.create_repo(cfg) # Pick an RPM with two versions. rpm_name = 'walrus' rpm_versions = _get_rpm_names_versions(cfg, _REPO_ID)[rpm_name] # Copy the older RPM to the second repository, and publish it. self._copy_and_publish(cfg, rpm_name, rpm_versions[0], repo_id) # Install the RPM on a host. repo_path = gen_yum_config_file( cfg, baseurl=urljoin(cfg.base_url, 'pulp/repos/' + repo_id), enabled=1, gpgcheck=0, metadata_expire=0, # force metadata to load every time repositoryid=repo_id, sslverify='yes' if cfg.verify else 'no', ) self.addCleanup( client.run, '{}rm {}'.format(sudo, repo_path).split() ) client.run('{}yum install -y {}'.format(sudo, rpm_name).split()) self.addCleanup( client.run, '{}yum remove -y {}'.format(sudo, rpm_name).split() ) client.run(['rpm', '-q', rpm_name]) # Copy the newer RPM to the second repository, and publish it. self._copy_and_publish(cfg, rpm_name, rpm_versions[1], repo_id) # Update the installed RPM on the host. proc = client.run('{}yum -y update {}'.format(sudo, rpm_name).split()) self.assertNotIn('Nothing to do.', proc.stdout)
def maybe_disable_selinux(self, cfg, pulp_issue_id): """Disable SELinux if appropriate. If the given Pulp issue is unresolved, and if SELinux is installed and enforcing on the target Pulp system, then disable SELinux and schedule it to be re-enabled. (Method ``addCleanup`` is used for the schedule.) :param pulp_smash.config.ServerConfig cfg: Information about the Pulp server being targeted. :param pulp_issue_id: The (integer) ID of a `Pulp issue`_. If the referenced issue is fixed in the Pulp system under test, this method immediately returns. :returns: Nothing. .. _Pulp issue: https://pulp.plan.io/issues/ """ # Abort if the Pulp issue is resolved, if SELinux is not installed or # if SELinux is not enforcing. # # NOTE: Hard-coding the absolute path to a command is a Bad Idea™. # However, non-login non-root shells may have short PATH environment # variables. For example: # # /usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/bin # # We cannot execute `PATH=${PATH}:/usr/sbin which getenforce` because # Plumbum does a good job of preventing shell expansions. See: # https://github.com/PulpQE/pulp-smash/issues/89 if selectors.bug_is_testable(pulp_issue_id, cfg.version): return client = cli.Client(cfg, cli.echo_handler) cmd = 'test -e /usr/sbin/getenforce'.split() if client.run(cmd).returncode != 0: return client.response_handler = cli.code_handler cmd = ['/usr/sbin/getenforce'] if client.run(cmd).stdout.strip().lower() != 'enforcing': return # Temporarily disable SELinux. sudo = '' if utils.is_root(cfg) else 'sudo ' cmd = (sudo + 'setenforce 0').split() client.run(cmd) cmd = (sudo + 'setenforce 1').split() self.addCleanup(client.run, cmd)
def maybe_disable_selinux(self, cfg, pulp_issue_id): """Disable SELinux if appropriate. If the given Pulp issue is unresolved, and if SELinux is installed and enforcing on the target Pulp system, then disable SELinux and schedule it to be re-enabled. (Method ``addCleanup`` is used for the schedule.) :param pulp_smash.config.PulpSmashConfig cfg: Information about the Pulp deployment being targeted. :param pulp_issue_id: The (integer) ID of a `Pulp issue`_. If the referenced issue is fixed in the Pulp system under test, this method immediately returns. :returns: Nothing. .. _Pulp issue: https://pulp.plan.io/issues/ """ # Abort if the Pulp issue is resolved, if SELinux is not installed or # if SELinux is not enforcing. # # NOTE: Hard-coding the absolute path to a command is a Bad Idea™. # However, non-login non-root shells may have short PATH environment # variables. For example: # # /usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/bin # # We cannot execute `PATH=${PATH}:/usr/sbin which getenforce` because # Plumbum does a good job of preventing shell expansions. See: # https://github.com/PulpQE/pulp-smash/issues/89 if selectors.bug_is_testable(pulp_issue_id, cfg.pulp_version): return client = cli.Client(cfg, cli.echo_handler) cmd = 'test -e /usr/sbin/getenforce'.split() if client.run(cmd).returncode != 0: return client.response_handler = cli.code_handler cmd = ['/usr/sbin/getenforce'] if client.run(cmd).stdout.strip().lower() != 'enforcing': return # Temporarily disable SELinux. sudo = '' if utils.is_root(cfg) else 'sudo ' cmd = (sudo + 'setenforce 0').split() client.run(cmd) cmd = (sudo + 'setenforce 1').split() self.addCleanup(client.run, cmd)
def test_all(self): """Test puppet_install_distributor. Do the following: 1. Create a puppet repository with a puppet_install_distributor 2. Upload a puppet module 3. Publish the repository 4. Check if the puppet_install_distributor config was properly used """ cli_client = cli.Client(self.cfg) sudo = () if utils.is_root(self.cfg) else ('sudo', ) # Create a directory and make sure Pulp can write to it. install_path = cli_client.run(('mktemp', '--directory')).stdout.strip() self.addCleanup(cli_client.run, sudo + ('rm', '-rf', install_path)) cli_client.run(sudo + ('chown', 'apache:apache', install_path)) cli_client.run(sudo + ('chcon', '-t', 'puppet_etc_t', install_path)) # Make sure the pulp_manage_puppet boolean is enabled cli_client.run(sudo + ('semanage', 'boolean', '--modify', '--on', 'pulp_manage_puppet')) self.addCleanup( cli_client.run, sudo + ('semanage', 'boolean', '--modify', '--off', 'pulp_manage_puppet')) # Create and populate a Puppet repository. distributor = gen_install_distributor() distributor['distributor_config']['install_path'] = install_path body = gen_repo() body['distributors'] = [distributor] client = api.Client(self.cfg, api.json_handler) repo = client.post(REPOSITORY_PATH, body) self.addCleanup(client.delete, repo['_href']) repo = client.get(repo['_href'], params={'details': True}) unit = utils.http_get(PUPPET_MODULE_URL_1) utils.upload_import_unit(self.cfg, unit, {'unit_type_id': 'puppet_module'}, repo) # Publish, and verify the module is present. (Dir has 700 permissions.) utils.publish_repo(self.cfg, repo) proc = cli_client.run(sudo + ('runuser', '--shell', '/bin/sh', '--command', 'ls -1 {}'.format(install_path), '-', 'apache')) self.assertIn(PUPPET_MODULE_1['name'], proc.stdout.split('\n'), proc)
def make_user(self, cfg): """Create a user account with a home directory and an SSH keypair. In addition, schedule the user for deletion with ``self.addCleanup``. :param pulp_smash.config.ServerConfig cfg: Information about the server being targeted. :returns: A ``(username, private_key)`` tuple. """ sudo = '' if utils.is_root(cfg) else 'sudo ' creator = _make_user(cfg) username = next(creator) self.addCleanup( cli.Client(cfg).run, (sudo + 'userdel --remove {}').format(username).split() ) private_key = next(creator) return (username, private_key)
def setUpClass(cls): """Find a RPM with more than one version on repo1.""" super(CopyAndPublishTwoVersionsRepoTestCase, cls).setUpClass() cls.client = cli.Client(cls.cfg) cls.sudo = '' if is_root(cls.cfg) else 'sudo ' # Retrieve all modules with multiple versions in the repo1. rpms = { key: value for key, value in _get_rpm_names_versions( cls.cfg, _REPO_ID).items() if len(value) > 1 } assert len(rpms) > 0 # Choose a random module with multiple versions. cls.rpm_name = random.choice(list(rpms.keys())) versions = rpms[cls.rpm_name] versions.sort() cls.rpm_old_version = versions[-2] cls.rpm_new_version = versions[-1]
def _create_repo_file(self, cfg, repo): """Create a file in ``/etc/yum.repos.d/`` referencing the repository. Also, schedule it for deletion. Return nothing. """ verify = cfg.get_systems('api')[0].roles['api'].get('verify') sudo = () if utils.is_root(cfg) else ('sudo', ) repo_path = gen_yum_config_file( cfg, baseurl=urljoin( cfg.get_base_url(), urljoin('pulp/repos/', repo['distributors'][0]['config']['relative_url'])), enabled=1, gpgcheck=0, metadata_expire=0, # force metadata to load every time repositoryid=repo['id'], sslverify='yes' if verify else 'no', ) self.addCleanup(cli.Client(cfg).run, sudo + ('rm', repo_path))
def test_all(self): """Publish a repository several times with the rsync distributor.""" # Create a user and a repository. Sync the repo. ssh_user, priv_key = self.make_user(self.cfg) ssh_identity_file = self.write_private_key(self.cfg, priv_key) repo_href = self.make_repo(self.cfg, { 'host': urlparse(self.cfg.base_url).netloc, 'root': '/home/' + ssh_user, 'ssh_identity_file': ssh_identity_file, 'ssh_user': ssh_user, }) utils.sync_repo(self.cfg, repo_href) # Publish with the yum and rsync distributors. api_client = api.Client(self.cfg) dists_by_type_id = _get_dists_by_type_id(self.cfg, repo_href) for type_id in ('yum_distributor', 'rpm_rsync_distributor'): body = {'id': dists_by_type_id[type_id]['id']} api_client.post(urljoin(repo_href, 'actions/publish/'), body) # Verify what the rsync distributor has done cli_client = cli.Client(self.cfg) sudo = '' if utils.is_root(self.cfg) else 'sudo ' cmd = sudo + 'ls -1 /home/{}'.format(ssh_user) dirs = set(cli_client.run(cmd.split()).stdout.strip().split('\n')) self.assertGreaterEqual(dirs, {'content'}) cmd = sudo + 'ls -1 /home/{}/content'.format(ssh_user) dirs = set(cli_client.run(cmd.split()).stdout.strip().split('\n')) self.assertGreaterEqual(dirs, {'units'}) cmd = sudo + 'ls -1 /home/{}/content/units'.format(ssh_user) dirs = set(cli_client.run(cmd.split()).stdout.strip().split('\n')) self.assertGreaterEqual(dirs, {'rpm'}) cmd = (sudo + ( 'find /home/{}/content/units/rpm/ -name *.rpm'.format(ssh_user) )) files = cli_client.run(cmd.split()).stdout.strip().split('\n') self.assertEqual(len(files), 32, files)
def _do_test(self, file_, label, recursive=False): """Assert that certain files have a label of ``label``. Get the SELinux label of the given ``file_``, or all files rooted at ``file_` if ``recursive`` is true. For each SELinux label, strip off the leading "user" portion of the label, and assert that the result — a string in the form :role:type:level — has the given ``label``. """ # Typical output: # # # getfattr --name=security.selinux /etc/passwd # getfattr: Removing leading '/' from absolute path names # # file: etc/passwd # security.selinux="system_u:object_r:passwd_file_t:s0" # cmd = [] if utils.is_root(config.get_config()) else ['sudo'] cmd.extend(('getfattr', '--name=security.selinux')) if recursive: cmd.append('--recursive') cmd.append(file_) lines = self.client.run(cmd).stdout.splitlines() matches = 0 getfattr_file = None # tracks file currently under consideration for line in lines: match = self.file_matcher.match(line) if match is not None: getfattr_file = match.groups(1) continue match = self.label_matcher.match(line) if match is not None: matches += 1 # Strip "user" prefix from label. For example: # user:role:type:level → :role:type:level file_label = match.group(1) file_label = file_label[file_label.find(':'):] self.assertEqual(file_label, label, getfattr_file) self.assertGreater(matches, 0, lines)
def _make_user(cfg): """Create a user account on a target system. This method is implemented as a generator. When executed, it will yield a username and private key. The corresponding public key is made the one and only key in the user's ``authorized_keys`` file. The username and private key are yielded one-by-one rather than as a pair because the user creation and key creation steps are executed serially. Should the latter fail, the calling function will still be able to delete the created user. The user is given a home directory. When deleting this user, make sure to pass ``--remove`` to ``userdel``. Otherwise, the home directory will be left in place. """ client = cli.Client(cfg) sudo = '' if utils.is_root(cfg) else 'sudo ' # According to useradd(8), usernames may be up to 32 characters long. # But long names break the rsync publish process: (SNIP == username) # # unix_listener: # "/tmp/rsync_distributor-[SNIP]@example.com:22.64tcAiD8em417CiN" # too long for Unix domain socket # username = utils.uuid4()[:12] cmd = 'useradd --create-home {0}' client.run((sudo + cmd.format(username)).split()) yield username cmd = 'runuser --shell /bin/sh {} --command'.format(username) cmd = (sudo + cmd).split() cmd.append('ssh-keygen -N "" -f /home/{}/.ssh/mykey'.format(username)) client.run(cmd) cmd = 'cp /home/{0}/.ssh/mykey.pub /home/{0}/.ssh/authorized_keys' client.run((sudo + cmd.format(username)).split()) cmd = 'cat /home/{0}/.ssh/mykey' private_key = client.run((sudo + cmd.format(username)).split()).stdout yield private_key
def _make_user(cfg): """A generator to create a user account on the target system. Yield a username and private key. The corresponding public key is made the one and only key in the user's ``authorized_keys`` file. The username and private key are yielded one-by-one rather than as a pair because the user creation and key creation steps are executed serially. Should the latter fail, the calling function will still be able to delete the created user. The user is given a home directory. When deleting this user, make sure to pass ``--remove`` to ``userdel``. Otherwise, the home directory will be left in place. """ client = cli.Client(cfg) sudo = '' if utils.is_root(cfg) else 'sudo ' # According to useradd(8), usernames may be up to 32 characters long. But # long names break the rsync publish process: (SNIP == username) # # unix_listener: # "/tmp/rsync_distributor-[SNIP]@example.com:22.64tcAiD8em417CiN" # too long for Unix domain socket # username = utils.uuid4()[:12] cmd = 'useradd --create-home {0}' client.run((sudo + cmd.format(username)).split()) yield username cmd = 'runuser --shell /bin/sh {} --command'.format(username) cmd = (sudo + cmd).split() cmd.append('ssh-keygen -N "" -f /home/{}/.ssh/mykey'.format(username)) client.run(cmd) cmd = 'cp /home/{0}/.ssh/mykey.pub /home/{0}/.ssh/authorized_keys' client.run((sudo + cmd.format(username)).split()) cmd = 'cat /home/{0}/.ssh/mykey' private_key = client.run((sudo + cmd.format(username)).split()).stdout yield private_key
def delete_user(cfg, username): """Delete a user. The Pulp rsync distributor has a habit of leaving (idle?) SSH sessions open even after publishing a repository. When executed, this function will: 1. Poll the process list until all processes belonging to ``username`` have died, or raise a ``unittest.SkipTest`` exception if the time limit is exceeded. 2. Delete ``username``. """ sudo = () if utils.is_root(cfg) else ('sudo', ) client = cli.Client(cfg) # values are arbitrary iter_time = 2 # seconds iter_limit = 15 # unitless # Wait for user's processes to die. cmd = sudo + ('ps', '-wwo', 'args', '--user', username, '--no-headers') i = 0 while i <= iter_limit: try: user_processes = client.run(cmd).stdout.splitlines() except exceptions.CalledProcessError: break i += 1 time.sleep(iter_time) else: raise unittest.SkipTest( 'User still has processes running after {}+ seconds. Aborting ' 'test. User processes: {}'.format(iter_time * iter_limit, user_processes)) # Delete user. cmd = sudo + ('userdel', '--remove', username) client.run(cmd)
def do_test(self, distributor_config_update): """Implement most of the test logic.""" rpms = tuple( utils.http_get(url) for url in (RPM_UNSIGNED_URL, RPM2_UNSIGNED_URL)) # Create a repository. client = api.Client(self.cfg, api.json_handler) body = gen_repo() body['distributors'] = [gen_distributor()] body['distributors'][0]['distributor_config'].update( distributor_config_update) repo = client.post(REPOSITORY_PATH, body) self.addCleanup(client.delete, repo['_href']) repo = client.get(repo['_href'], params={'details': True}) # Upload an RPM, publish the repo, and count metadata files twice. cli_client = cli.Client(self.cfg) sudo = () if utils.is_root(self.cfg) else ('sudo', ) find_repodata_cmd = sudo + ( 'find', os.path.join('/var/lib/pulp/published/yum/master/yum_distributor/', str(repo['id'])), '-type', 'd', '-name', 'repodata') found = [] for rpm in rpms: utils.upload_import_unit( self.cfg, rpm, {'unit_type_id': 'rpm'}, repo, ) utils.publish_repo(self.cfg, repo) repodata_path = cli_client.run(find_repodata_cmd).stdout.strip() found.append( cli_client.run(sudo + ('find', repodata_path, '-type', 'f')).stdout.splitlines()) return found
def test_all(self): """Publish the rpm rsync distributor before the yum distributor.""" # Create a user and a repository. ssh_user, priv_key = self.make_user(self.cfg) ssh_identity_file = self.write_private_key(self.cfg, priv_key) repo_href = self.make_repo(self.cfg, { 'host': urlparse(self.cfg.base_url).netloc, 'root': '/home/' + ssh_user, 'ssh_identity_file': ssh_identity_file, 'ssh_user': ssh_user, }) # Publish with the rsync distributor. dists_by_type_id = _get_dists_by_type_id(self.cfg, repo_href) with self.assertRaises(exceptions.TaskReportError): api.Client(self.cfg).post(urljoin(repo_href, 'actions/publish/'), { 'id': dists_by_type_id['rpm_rsync_distributor']['id'], }) # Verify that the rsync distributor hasn't placed files sudo = '' if utils.is_root(self.cfg) else 'sudo ' cmd = (sudo + 'ls -1 /home/{}'.format(ssh_user)).split() dirs = set(cli.Client(self.cfg).run(cmd).stdout.strip().split('\n')) self.assertNotIn('content', dirs)
def test_true(self): """Assert the method returns ``True`` when root.""" with mock.patch.object(cli, 'Client') as clien: clien.return_value.run.return_value.stdout.strip.return_value = '0' self.assertTrue(utils.is_root(None))
def test_false(self): """Assert the method returns ``False`` when non-root.""" with mock.patch.object(cli, 'Client') as clien: clien.return_value.run.return_value.stdout.strip.return_value = '1' self.assertFalse(utils.is_root(None))