osbuild-depsolve-dnf: add SBOM support for DNF5

Enable generating SBOM documents for depsolved transactions when using
DNF5. Enable SBOM testing with DNF5 in unit tests.

Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
Tomáš Hozza 2024-11-11 14:39:41 +01:00 committed by Simon de Vlieger
parent def6a9fabd
commit 562a30ce93
3 changed files with 18 additions and 17 deletions

View file

@ -2,7 +2,7 @@ import os
import os.path import os.path
import tempfile import tempfile
from datetime import datetime from datetime import datetime
from typing import List from typing import Dict, List
import libdnf5 as dnf5 import libdnf5 as dnf5
from libdnf5.base import GoalProblem_NO_PROBLEM as NO_PROBLEM from libdnf5.base import GoalProblem_NO_PROBLEM as NO_PROBLEM
@ -19,6 +19,8 @@ from osbuild.solver import (
modify_rootdir_path, modify_rootdir_path,
read_keys, read_keys,
) )
from osbuild.util.sbom.dnf5 import dnf_pkgset_to_sbom_pkgset
from osbuild.util.sbom.spdx import bom_pkgset_to_spdx2_doc
def remote_location(package, schemes=("http", "ftp", "file", "https")): def remote_location(package, schemes=("http", "ftp", "file", "https")):
@ -276,6 +278,17 @@ class DNF5(SolverBase):
def _timestamp_to_rfc3339(timestamp): def _timestamp_to_rfc3339(timestamp):
return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%dT%H:%M:%SZ') return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%dT%H:%M:%SZ')
@staticmethod
def _sbom_for_pkgset(pkgset: List[dnf5.rpm.Package]) -> Dict:
"""
Create an SBOM document for the given package set.
For now, only SPDX v2 is supported.
"""
pkgset = dnf_pkgset_to_sbom_pkgset(pkgset)
spdx_doc = bom_pkgset_to_spdx2_doc(pkgset)
return spdx_doc.to_dict()
def dump(self): def dump(self):
"""dump returns a list of all available packages""" """dump returns a list of all available packages"""
packages = [] packages = []
@ -445,4 +458,8 @@ class DNF5(SolverBase):
"packages": packages, "packages": packages,
"repos": repositories, "repos": repositories,
} }
if "sbom" in arguments:
response["sbom"] = self._sbom_for_pkgset(last_transaction)
return response return response

View file

@ -161,15 +161,6 @@ def validate_request(request):
sbom = request["arguments"].get("sbom") sbom = request["arguments"].get("sbom")
if sbom is not None: if sbom is not None:
# NB: check the DNF5 flag here, instead of in the dnf5 module,
# to consistently return this error message, even if there are other
# potential errors in the request, such as broken repository.
if config.get("use_dnf5", False):
return {
"kind": "InvalidRequest",
"reason": "SBOM support for DNF5 is not implemented"
}
if command != "depsolve": if command != "depsolve":
return { return {
"kind": "InvalidRequest", "kind": "InvalidRequest",

View file

@ -1280,13 +1280,6 @@ def test_depsolve(tmp_path, repo_servers, dnf_config, detect_fn, with_sbom, test
res, exit_code = depsolve(transactions, repo_configs, root_dir, res, exit_code = depsolve(transactions, repo_configs, root_dir,
cache_dir, dnf_config, opt_metadata, with_sbom) cache_dir, dnf_config, opt_metadata, with_sbom)
# NB: dnf5 implementation does not support SBOM yet
if dnf_config.get("use_dnf5", False) and with_sbom:
assert exit_code != 0
assert res["kind"] == "InvalidRequest"
assert res["reason"] == "SBOM support for DNF5 is not implemented"
continue
if test_case.get("error", False): if test_case.get("error", False):
assert exit_code != 0 assert exit_code != 0
assert res["kind"] == test_case["error_kind"] assert res["kind"] == test_case["error_kind"]