abrt/faf

reposync fails parsing certain rpm dependencies

Closed this issue · 11 comments

I think this is a corner case we might be facing ourselves which might be too specific but I think package version parsing could be causing the following:

[2020-06-10 14:37:58] DEBUG:faf.pyfaf.faf_rpm: php-oracle-1.0-4.el7.cern.x86_64 contains 5 files
[2020-06-10 14:37:58] CRITICAL:faf: Action failed unexpectedly: DataError: (psycopg2.errors.InvalidTextRepresentation) invalid input syntax for integer: "%{epoch}"
LINE 1: ...25, 'REQUIRES', 'oracle-instantclient-basic', 12, '%{epoch}'...
                                                             ^

[SQL: INSERT INTO packagedependencies (package_id, type, name, flags, epoch, version, release) VALUES (%(package_id)s, %(type)s, %(name)s, %(flags)s, %(epoch)s, %(version)s, %(release)s) RETURNING packagedependencies.id]
[parameters: {'package_id': 2825, 'type': 'REQUIRES', 'name': 'oracle-instantclient-basic', 'flags': 12, 'epoch': '%{epoch}', 'version': '12.1', 'release': None}]
(Background on this error at: http://sqlalche.me/e/9h9h)
Traceback (most recent call last):
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/base.py", line 1283, in _execute_context
    self.dialect.do_execute(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/default.py", line 590, in do_execute
    cursor.execute(statement, parameters)
psycopg2.errors.InvalidTextRepresentation: invalid input syntax for integer: "%{epoch}"
LINE 1: ...25, 'REQUIRES', 'oracle-instantclient-basic', 12, '%{epoch}'...
                                                             ^


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/bin/faf", line 152, in <module>
    main()
  File "/usr/bin/faf", line 132, in main
    exitcode = cmdline.func(cmdline, db)
  File "/usr/lib/python3.8/site-packages/pyfaf/actions/reposync.py", line 240, in run
    res = store_rpm_deps(db, package, repo_instance['nogpgcheck'])
  File "/usr/lib/python3.8/site-packages/pyfaf/faf_rpm.py", line 133, in store_rpm_deps
    db.session.flush()
  File "/usr/lib/python3.8/site-packages/pyfaf/storage/__init__.py", line 104, in _flush_session
    self.session._flush_orig(*args, **kwargs) #pylint: disable=protected-access
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/orm/session.py", line 2523, in flush
    self._flush(objects)
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/orm/session.py", line 2664, in _flush
    transaction.rollback(_capture_exception=True)
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 68, in __exit__
    compat.raise_(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/util/compat.py", line 178, in raise_
    raise exception
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/orm/session.py", line 2624, in _flush
    flush_context.execute()
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 422, in execute
    rec.execute(self)
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 586, in execute
    persistence.save_obj(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/orm/persistence.py", line 239, in save_obj
    _emit_insert_statements(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/orm/persistence.py", line 1135, in _emit_insert_statements
    result = cached_connections[connection].execute(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/base.py", line 1020, in execute
    return meth(self, multiparams, params)
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/sql/elements.py", line 298, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/base.py", line 1133, in _execute_clauseelement
    ret = self._execute_context(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/base.py", line 1323, in _execute_context
    self._handle_dbapi_exception(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/base.py", line 1517, in _handle_dbapi_exception
    util.raise_(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/util/compat.py", line 178, in raise_
    raise exception
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/base.py", line 1283, in _execute_context
    self.dialect.do_execute(
  File "/usr/lib64/python3.8/site-packages/sqlalchemy/engine/default.py", line 590, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.DataError: (psycopg2.errors.InvalidTextRepresentation) invalid input syntax for integer: "%{epoch}"
LINE 1: ...25, 'REQUIRES', 'oracle-instantclient-basic', 12, '%{epoch}'...
                                                             ^

[SQL: INSERT INTO packagedependencies (package_id, type, name, flags, epoch, version, release) VALUES (%(package_id)s, %(type)s, %(name)s, %(flags)s, %(epoch)s, %(version)s, %(release)s) RETURNING packagedependencies.id]
[parameters: {'package_id': 2825, 'type': 'REQUIRES', 'name': 'oracle-instantclient-basic', 'flags': 12, 'epoch': '%{epoch}', 'version': '12.1', 'release': None}]
(Background on this error at: http://sqlalche.me/e/9h9h)

As a reference these are the dependencies:

[root@c7juareztest ~]# yum -q deplist http://linuxsoft.cern.ch/cern/centos/7/cernonly/x86_64/Packages/php-oracle-1.0-4.el7.cern.x86_64.rpm

package: php-oracle.x86_64 1.0-4.el7.cern
  dependency: /usr/lib64/php/modules
   provider: php-common.x86_64 5.4.16-48.el7
  dependency: config(php-oracle) = 1.0-4.el7.cern
   provider: php-oracle.x86_64 1.0-4.el7.cern
  dependency: libc.so.6()(64bit)
   provider: glibc.x86_64 2.17-307.el7.1
  dependency: libc.so.6(GLIBC_2.2.5)(64bit)
   provider: glibc.x86_64 2.17-307.el7.1
  dependency: libclntsh.so.12.1()(64bit)
   provider: oracle-instantclient12.2-meta.x86_64 2:19.3-3.el7.cern
   provider: oracle-instantclient12.1-meta.x86_64 2:12.1-12.el7.cern
   provider: oracle-instantclient12.1.x86_64 1:1-1.el7.cern
   provider: oracle-instantclient-basic.x86_64 12.2-1.el7.cern
  dependency: oracle-instantclient-basic >= 12.1
   provider: oracle-instantclient-basic.x86_64 2:19.3-3.el7.cern
  dependency: php-api = 20100412
   provider: php-common.x86_64 5.4.16-48.el7
  dependency: rtld(GNU_HASH)
   provider: glibc.x86_64 2.17-307.el7.1
   provider: glibc.i686 2.17-307.el7.1

Bear in mind this is an internal repo limited to our nodes, cannot be checked from the outside.

It seems that code responsible for dealing with 'flags', 'epoch', 'version' and 'release' might be causing this.

At least a simple catch should be added just in case

As a workaround we are excluding the repo containing this rpm, which is quite not ideal as even if the mentioned rpm is not important, the rest of them in the repo are, but there is no exclusion mechanism, as the prefix option cannot be used for this purpose.

Not sure about it but this uncatched exception could be breaking consistency on the database, as consecutive re-runs are failing due to [2020-06-10 15:31:00] CRITICAL:faf: Action failed unexpectedly: IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "buildopsysreleasearch_pkey" DETAIL: Key (build_id, opsysrelease_id, arch_id)=(3228, 5, 3) already exists. as if something should have been flushed to DB.

It seems to be caused by the rpm naming, as it is parsed by: Parse epoch:version-release according to rpmUtils.miscutils.stringToVersion()

So, I assume, given its internal nature, the package could not even be shared somewhere else?

Hmm, so I found rpm-software-management/rpm@be0c4b5. We could probably dismantle the (possibly broken) parsing code (once a new RPM release is cut with that code).

So, I assume, given its internal nature, the package could not even be shared somewhere else?

It cannot I am afraid. In any case any package requiring packages such as https://download.oracle.com/otn_software/linux/instantclient/19600/oracle-instantclient19.6-basic-19.6.0.0.0-1.x86_64.rpm or similar namings as a dependency should do the trick.

Yeah, I cannot glean anything wrong from that package, I’m afraid.

I will try to debug to get you as much information as possible to retrieve as much information as possible.

Maybe you can help me out by replicating the piece of code and sharing the output:

import pyfaf.faf_rpm
import rpm

package = open('php-oracle-1.0-4.el7.cern.x86_64.rpm')
ts = rpm.ts()
header = ts.hdrFromFdno(package.fileno())
dependencies = header.dsFromHeader('requirename')
for dependency in dependencies:
    name = dependency.N()
    flags = dependency.Flags()
    evr = dependency.EVR()
    epoch, version, release = pyfaf.faf_rpm.parse_evr(evr)

    print('name: %s' % name)
    print('flags: %d' % flags)
    print('evr: %s' % evr)
    print('parsed evr: (%s, %s, %s)' % (epoch, version, release))
    print()
name: /usr/lib64/php/modules
flags: 0
evr: 
parsed evr: (None, None None)
name: config(php-oracle)
flags: 268435464
evr: 1.0-4.el7.cern
parsed evr: (0, 1.0 4.el7.cern)
name: libc.so.6()(64bit)
flags: 16384
evr: 
parsed evr: (None, None None)
name: libc.so.6(GLIBC_2.2.5)(64bit)
flags: 16384
evr: 
parsed evr: (None, None None)
name: libclntsh.so.12.1()(64bit)
flags: 16384
evr: 
parsed evr: (None, None None)
name: oracle-instantclient-basic
flags: 12
evr: %{epoch}:12.1
parsed evr: (%{epoch}, 12.1 None)
name: php-api
flags: 8
evr: 20100412
parsed evr: (0, 20100412 None)
name: rpmlib(CompressedFileNames)
flags: 16777226
evr: 3.0.4-1
parsed evr: (0, 3.0.4 1)
name: rpmlib(FileDigests)
flags: 16777226
evr: 4.6.0-1
parsed evr: (0, 4.6.0 1)
name: rpmlib(PayloadFilesHavePrefix)
flags: 16777226
evr: 4.0-1
parsed evr: (0, 4.0 1)
name: rtld(GNU_HASH)
flags: 16384
evr: 
parsed evr: (None, None None)
name: rpmlib(PayloadIsXz)
flags: 16777226
evr: 5.2-1
parsed evr: (0, 5.2 1)

So... I guess this is because of whoever built this rpm... Funny enough, it installs:

package: php-oracle-1.0-4.el7.cern.x86_64
  dependency: /usr/lib64/php/modules
   provider: php-common-7.4.6-1.fc32.x86_64
   provider: owfs-php-3.2p3-5.fc32.x86_64
  dependency: config(php-oracle) = 1.0-4.el7.cern
   provider: php-oracle-1.0-4.el7.cern.x86_64
  dependency: libc.so.6()(64bit)
   provider: glibc-2.31-2.fc32.x86_64
  dependency: libc.so.6(GLIBC_2.2.5)(64bit)
   provider: glibc-2.31-2.fc32.x86_64
  dependency: libclntsh.so.12.1()(64bit)
  dependency: oracle-instantclient-basic >= %{epoch}:12.1
  dependency: php-api = 20100412
  dependency: rpmlib(CompressedFileNames) <= 3.0.4-1
  dependency: rpmlib(FileDigests) <= 4.6.0-1
  dependency: rpmlib(PayloadFilesHavePrefix) <= 4.0-1
  dependency: rpmlib(PayloadIsXz) <= 5.2-1
  dependency: rtld(GNU_HASH)
   provider: glibc-2.31-2.fc32.i686
   provider: glibc-2.31-2.fc32.x86_64

You can just forget about it in that case... I guess the only thing to do would be to ignore the package if it breaks reposync..., a try-catch would do. Or some kind of --skipbroken

So this at least tells us that garbage comes directly from RPM. Basically, it’s true that the package is broken:

https://bugzilla.redhat.com/show_bug.cgi?id=1251453
rpm-software-management/rpm@f5bab7c

The version of rpm in CentOS 7 does not contain that fix and still allows undefined macros to slip through.

I guess adding some checks for packages that are broken for hysterical raisins really is the way to go.