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.