mirror of
https://github.com/resf/distro-tools.git
synced 2024-10-31 19:11:24 +00:00
Support updateinfo output
This commit is contained in:
parent
15c4ca576d
commit
ea2c37f66a
@ -13,6 +13,10 @@ from common.logger import Logger
|
||||
NVRA_RE = re.compile(
|
||||
r"^(\S+)-([\w~%.+]+)-(\w+(?:\.[\w~%+]+)+?)(?:\.(\w+))?(?:\.rpm)?$"
|
||||
)
|
||||
NEVRA_RE = re.compile(
|
||||
r"^(\S+)-(\d):([\w~%.+]+)-(\w+(?:\.[\w~%+]+)+?)(?:\.(\w+))?(?:\.rpm)?$"
|
||||
)
|
||||
EPOCH_RE = re.compile(r"(\d+):")
|
||||
DIST_RE = re.compile(r"(\.el\d(?:_\d|))")
|
||||
MODULE_DIST_RE = re.compile(r"\.module.+$")
|
||||
|
||||
|
@ -10,6 +10,7 @@ py_library(
|
||||
"routes/api_advisories.py",
|
||||
"routes/api_compat.py",
|
||||
"routes/api_red_hat.py",
|
||||
"routes/api_updateinfo.py",
|
||||
"routes/login.py",
|
||||
"routes/logout.py",
|
||||
"routes/red_hat_advisories.py",
|
||||
@ -28,13 +29,16 @@ py_library(
|
||||
deps = [
|
||||
"//apollo/db:db_lib",
|
||||
"//apollo/db/serialize:serialize_lib",
|
||||
"//apollo/rpmworker:rpmworker_lib",
|
||||
"//common:common_lib",
|
||||
"@pypi_fastapi//:pkg",
|
||||
"@pypi_fastapi_pagination//:pkg",
|
||||
"@pypi_itsdangerous//:pkg",
|
||||
"@pypi_jinja2//:pkg",
|
||||
"@pypi_passlib//:pkg",
|
||||
"@pypi_pydantic//:pkg",
|
||||
"@pypi_python_multipart//:pkg",
|
||||
"@pypi_python_slugify//:pkg",
|
||||
"@pypi_rssgen//:pkg",
|
||||
"@pypi_starlette//:pkg",
|
||||
"@pypi_tortoise_orm//:pkg",
|
||||
|
261
apollo/server/routes/api_updateinfo.py
Normal file
261
apollo/server/routes/api_updateinfo.py
Normal file
@ -0,0 +1,261 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from fastapi import APIRouter, Response
|
||||
from slugify import slugify
|
||||
|
||||
from apollo.db import AdvisoryAffectedProduct
|
||||
from apollo.server.settings import COMPANY_NAME, MANAGING_EDITOR, UI_URL, get_setting
|
||||
|
||||
from apollo.rpmworker.repomd import NEVRA_RE, NVRA_RE, EPOCH_RE
|
||||
|
||||
from common.fastapi import RenderErrorTemplateException
|
||||
|
||||
router = APIRouter(tags=["updateinfo"])
|
||||
|
||||
|
||||
@router.get("/{product_name}/{repo}/updateinfo.xml")
|
||||
async def get_updateinfo(
|
||||
product_name: str,
|
||||
repo: str,
|
||||
req_arch: Optional[str] = None,
|
||||
):
|
||||
filters = {
|
||||
"name": product_name,
|
||||
"advisory__packages__repo_name": repo,
|
||||
}
|
||||
if req_arch:
|
||||
filters["arch"] = req_arch
|
||||
|
||||
affected_products = await AdvisoryAffectedProduct.filter(
|
||||
**filters
|
||||
).prefetch_related(
|
||||
"advisory",
|
||||
"advisory__cves",
|
||||
"advisory__fixes",
|
||||
"advisory__packages",
|
||||
"supported_product",
|
||||
).all()
|
||||
if not affected_products:
|
||||
raise RenderErrorTemplateException("No advisories found", 404)
|
||||
|
||||
ui_url = await get_setting(UI_URL)
|
||||
managing_editor = await get_setting(MANAGING_EDITOR)
|
||||
company_name = await get_setting(COMPANY_NAME)
|
||||
|
||||
advisories = {}
|
||||
for affected_product in affected_products:
|
||||
advisory = affected_product.advisory
|
||||
if advisory.name not in advisories:
|
||||
advisories[advisory.name] = {
|
||||
"advisory":
|
||||
advisory,
|
||||
"arch":
|
||||
affected_product.arch,
|
||||
"major_version":
|
||||
affected_product.major_version,
|
||||
"minor_version":
|
||||
affected_product.minor_version,
|
||||
"supported_product_name":
|
||||
affected_product.supported_product.name,
|
||||
}
|
||||
|
||||
tree = ET.Element("updates")
|
||||
for _, adv in advisories.items():
|
||||
advisory = adv["advisory"]
|
||||
product_arch = adv["arch"]
|
||||
major_version = adv["major_version"]
|
||||
minor_version = adv["minor_version"]
|
||||
supported_product_name = adv["supported_product_name"]
|
||||
|
||||
update = ET.SubElement(tree, "update")
|
||||
|
||||
# Set update attributes
|
||||
update.set("from", managing_editor)
|
||||
update.set("status", "final")
|
||||
|
||||
if advisory.kind == "Security":
|
||||
update.set("type", "security")
|
||||
elif advisory.kind == "Bug Fix":
|
||||
update.set("type", "bugfix")
|
||||
elif advisory.kind == "Enhancement":
|
||||
update.set("type", "enhancement")
|
||||
|
||||
update.set("version", "2")
|
||||
|
||||
# Add id
|
||||
ET.SubElement(update, "id").text = advisory.name
|
||||
|
||||
# Add title
|
||||
ET.SubElement(update, "title").text = advisory.synopsis
|
||||
|
||||
# Add description
|
||||
ET.SubElement(update, "description").text = advisory.description
|
||||
|
||||
# Add time
|
||||
time_format = "%Y-%m-%d %H:%M:%S"
|
||||
ET.SubElement(update, "issued"
|
||||
).text = advisory.published_at.strftime(time_format)
|
||||
ET.SubElement(update, "updated"
|
||||
).text = advisory.updated_at.strftime(time_format)
|
||||
|
||||
# Add rights
|
||||
now = datetime.datetime.utcnow()
|
||||
ET.SubElement(
|
||||
update, "rights"
|
||||
).text = f"Copyright {now.year} {company_name}"
|
||||
|
||||
# Add release name
|
||||
release_name = f"{supported_product_name} {major_version}"
|
||||
if minor_version:
|
||||
release_name += f".{minor_version}"
|
||||
ET.SubElement(update, "release").text = release_name
|
||||
|
||||
# Add pushcount
|
||||
ET.SubElement(update, "pushcount").text = "1"
|
||||
|
||||
# Add severity
|
||||
ET.SubElement(update, "severity").text = advisory.severity
|
||||
|
||||
# Add summary
|
||||
ET.SubElement(update, "summary").text = advisory.topic
|
||||
|
||||
# Add description
|
||||
ET.SubElement(update, "description").text = advisory.description
|
||||
|
||||
# Add solution
|
||||
ET.SubElement(update, "solution").text = ""
|
||||
|
||||
# Add references
|
||||
references = ET.SubElement(update, "references")
|
||||
for cve in advisory.cves:
|
||||
reference = ET.SubElement(references, "reference")
|
||||
reference.set(
|
||||
"href",
|
||||
f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve.cve}",
|
||||
)
|
||||
reference.set("id", cve.cve)
|
||||
reference.set("type", "cve")
|
||||
reference.set("title", cve.cve)
|
||||
|
||||
for fix in advisory.fixes:
|
||||
reference = ET.SubElement(references, "reference")
|
||||
reference.set("href", fix.source)
|
||||
reference.set("id", fix.ticket_id)
|
||||
reference.set("type", "bugzilla")
|
||||
reference.set("title", fix.description)
|
||||
|
||||
# Add UI self reference
|
||||
reference = ET.SubElement(references, "reference")
|
||||
reference.set("href", f"{ui_url}/{advisory.name}")
|
||||
reference.set("id", advisory.name)
|
||||
reference.set("type", "self")
|
||||
reference.set("title", advisory.name)
|
||||
|
||||
# Add packages
|
||||
packages = ET.SubElement(update, "pkglist")
|
||||
|
||||
# Create collection
|
||||
collection = ET.SubElement(packages, "collection")
|
||||
collection_short = slugify(f"{product_name}-{repo}-rpms")
|
||||
collection.set("short", collection_short)
|
||||
|
||||
# Set short to name as well
|
||||
ET.SubElement(collection, "name").text = collection_short
|
||||
|
||||
pkg_name_map = {}
|
||||
for pkg in advisory.packages:
|
||||
if pkg.package_name not in pkg_name_map:
|
||||
pkg_name_map[pkg.package_name] = []
|
||||
|
||||
pkg_name_map[pkg.package_name].append(pkg)
|
||||
|
||||
pkg_src_rpm = {}
|
||||
for top_pkg in advisory.packages:
|
||||
if top_pkg.package_name not in pkg_src_rpm:
|
||||
top_nvra_no_epoch = EPOCH_RE.sub("", top_pkg.nevra)
|
||||
top_nvra = NVRA_RE.search(top_nvra_no_epoch)
|
||||
top_arch = top_nvra.group(4)
|
||||
|
||||
for pkg in pkg_name_map[top_pkg.package_name]:
|
||||
nvra_no_epoch = EPOCH_RE.sub("", pkg.nevra)
|
||||
nvra = NVRA_RE.search(nvra_no_epoch)
|
||||
if nvra:
|
||||
name = nvra.group(1)
|
||||
arch = nvra.group(4)
|
||||
if pkg.package_name == name and top_arch == arch:
|
||||
src_rpm = nvra_no_epoch
|
||||
if not src_rpm.endswith(".rpm"):
|
||||
src_rpm += ".rpm"
|
||||
pkg_src_rpm[pkg.package_name] = src_rpm
|
||||
|
||||
# If we encounter modules, we need to add them to the collection later
|
||||
modules = {}
|
||||
|
||||
for pkg in advisory.packages:
|
||||
if pkg.nevra.endswith(".src.rpm"):
|
||||
continue
|
||||
|
||||
name = pkg.package_name
|
||||
epoch = "0"
|
||||
if NEVRA_RE.match(pkg.nevra):
|
||||
nevra = NEVRA_RE.search(pkg.nevra)
|
||||
name = nevra.group(1)
|
||||
epoch = nevra.group(2)
|
||||
version = nevra.group(3)
|
||||
release = nevra.group(4)
|
||||
arch = nevra.group(5)
|
||||
elif NVRA_RE.match(pkg.nevra):
|
||||
nvra = NVRA_RE.search(pkg.nevra)
|
||||
name = nvra.group(1)
|
||||
version = nvra.group(2)
|
||||
release = nvra.group(3)
|
||||
arch = nvra.group(4)
|
||||
else:
|
||||
continue
|
||||
|
||||
if pkg.package_name not in pkg_src_rpm:
|
||||
continue
|
||||
|
||||
package = ET.SubElement(collection, "package")
|
||||
package.set("name", name)
|
||||
package.set("arch", arch)
|
||||
package.set("epoch", epoch)
|
||||
package.set("version", version)
|
||||
package.set("release", release)
|
||||
package.set("src", pkg_src_rpm[pkg.package_name])
|
||||
|
||||
# Add filename element
|
||||
ET.SubElement(package,
|
||||
"filename").text = EPOCH_RE.sub("", pkg.nevra)
|
||||
|
||||
# Add checksum
|
||||
ET.SubElement(
|
||||
package, "sum", type=pkg.checksum_type
|
||||
).text = pkg.checksum
|
||||
|
||||
# Check if module
|
||||
if pkg.module_name:
|
||||
modules[pkg.module_name] = {
|
||||
"name": pkg.module_name,
|
||||
"context": pkg.module_context,
|
||||
"stream": pkg.module_stream,
|
||||
"version": pkg.module_version,
|
||||
"arch": product_arch,
|
||||
}
|
||||
|
||||
# Add modules
|
||||
for module in modules.values():
|
||||
module_element = ET.Element("module")
|
||||
module_element.set("name", module["name"])
|
||||
module_element.set("stream", module["stream"])
|
||||
module_element.set("version", module["version"])
|
||||
module_element.set("context", module["context"])
|
||||
module_element.set("arch", module["arch"])
|
||||
collection.insert(1, module_element)
|
||||
|
||||
ET.indent(tree)
|
||||
xml_str = ET.tostring(tree, encoding="unicode", method="xml")
|
||||
|
||||
return Response(content=xml_str, media_type="application/xml")
|
@ -9,14 +9,15 @@ from fastapi.responses import JSONResponse
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
from fastapi_pagination import add_pagination
|
||||
|
||||
from apollo.server.routes.advisories import router as advisories_router
|
||||
from apollo.server.routes.statistics import router as statistics_router
|
||||
from apollo.server.routes.login import router as login_router
|
||||
from apollo.server.routes.logout import router as logout_router
|
||||
from apollo.server.routes.admin_index import router as admin_index_router
|
||||
from apollo.server.routes.api_advisories import router as api_advisories_router
|
||||
from apollo.server.routes.api_compat import router as api_compat_router
|
||||
from apollo.server.routes.api_updateinfo import router as api_updateinfo_router
|
||||
from apollo.server.routes.api_red_hat import router as api_red_hat_router
|
||||
from apollo.server.routes.advisories import router as advisories_router
|
||||
from apollo.server.routes.api_compat import router as api_compat_router
|
||||
from apollo.server.routes.red_hat_advisories import router as red_hat_advisories_router
|
||||
from apollo.server.settings import SECRET_KEY, SettingsMiddleware, get_setting
|
||||
from apollo.server.utils import admin_user_scheme, templates
|
||||
@ -49,8 +50,9 @@ app.include_router(
|
||||
)
|
||||
app.include_router(red_hat_advisories_router, prefix="/red_hat")
|
||||
app.include_router(api_advisories_router, prefix="/api/v3/advisories")
|
||||
app.include_router(api_compat_router, prefix="/v2/advisories")
|
||||
app.include_router(api_updateinfo_router, prefix="/api/v3/updateinfo")
|
||||
app.include_router(api_red_hat_router, prefix="/api/v3/red_hat")
|
||||
app.include_router(api_compat_router, prefix="/v2/advisories")
|
||||
|
||||
add_pagination(app)
|
||||
|
||||
|
@ -1128,6 +1128,9 @@ manifest:
|
||||
shellingham.posix.proc: shellingham
|
||||
shellingham.posix.ps: shellingham
|
||||
six: six
|
||||
slugify: python_slugify
|
||||
slugify.slugify: python_slugify
|
||||
slugify.special: python_slugify
|
||||
sniffio: sniffio
|
||||
soupsieve: soupsieve
|
||||
soupsieve.css_match: soupsieve
|
||||
@ -1303,6 +1306,7 @@ manifest:
|
||||
temporalio.worker.workflow_sandbox: temporalio
|
||||
temporalio.workflow: temporalio
|
||||
test_autoflake: autoflake
|
||||
text_unidecode: text_unidecode
|
||||
toml: toml
|
||||
toml.decoder: toml
|
||||
toml.encoder: toml
|
||||
@ -1503,4 +1507,4 @@ manifest:
|
||||
yarl: yarl
|
||||
pip_repository:
|
||||
name: pypi
|
||||
integrity: 98955591e0f143193fb26aa58a3c5c9120c83f3270459709acd849f9613119ac
|
||||
integrity: 8d848d11c467949c981e296a23211f42a009566f0d2ab5fe11ceb59ade4cb74e
|
||||
|
@ -18,3 +18,4 @@ itsdangerous==2.1.2
|
||||
PyYAML==6.0
|
||||
beautifulsoup4==4.11.2
|
||||
rssgen==0.9.0
|
||||
python-slugify==8.0.0
|
@ -653,6 +653,10 @@ python-dateutil==2.8.2 \
|
||||
python-multipart==0.0.5 \
|
||||
--hash=sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43
|
||||
# via -r ./requirements.txt
|
||||
python-slugify==8.0.0 \
|
||||
--hash=sha256:51f217508df20a6c166c7821683384b998560adcf8f19a6c2ca8b460528ccd9c \
|
||||
--hash=sha256:f1da83f3c7ab839b3f84543470cd95bdb5a81f1a0b80fed502f78b7dca256062
|
||||
# via -r ./requirements.txt
|
||||
pytz==2022.7.1 \
|
||||
--hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \
|
||||
--hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a
|
||||
@ -746,6 +750,10 @@ temporalio==1.0.0 \
|
||||
--hash=sha256:7c82a875c3db9ab2c8492ddc01498dbb2636cad34cf8bc985a6f0f17bd627f99 \
|
||||
--hash=sha256:b2454ef6b68335a554adca1e4f14831b5c3ea33ef8adb25742dd91652bd38a82
|
||||
# via -r ./requirements.txt
|
||||
text-unidecode==1.3 \
|
||||
--hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 \
|
||||
--hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93
|
||||
# via python-slugify
|
||||
toml==0.10.2 \
|
||||
--hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \
|
||||
--hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f
|
||||
|
Loading…
Reference in New Issue
Block a user