Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A library for accessing push items from various sources.
sources/koji
sources/registry
sources/errata
sources/konflux
sources/pub
model/base
model/files
Expand Down
99 changes: 99 additions & 0 deletions docs/sources/konflux.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
Source: konflux
================

The ``konflux`` push source allows the loading of content from local JSON files
organized by advisory. This source is designed for use with Konflux-generated
advisory metadata and does not require network access or external API calls.

Supported content types:

* RPMs
* Advisories

The source is designed to be extensible and can support additional content types
(such as modules, container images, etc.) in the future as needed.

konflux source URLs
-------------------

The base form of a konflux source URL is:

``konflux:base-directory?advisories=RHXA-XXXX:0001[,RHXA-XXXX:0002[,...]]``

For example, referencing a single advisory would look like:

``konflux:/path/to/konflux/data?advisories=RHSA-2020:0509``

Multiple advisories can be specified with a comma-separated list:

``konflux:/path/to/konflux/data?advisories=RHSA-2020:0509,RHSA-2020:0510``

The base directory should contain subdirectories named after each advisory ID.
Each advisory subdirectory must contain:

* ``advisory_cdn_metadata.json`` - Advisory metadata (title, severity, references, packages, etc.)
* ``advisory_cdn_filelist.json`` - RPM file list with checksums, signing keys, and repository destinations

Directory structure
...................

Example directory structure::

/path/to/konflux/data/
├── RHSA-2020:0509/
│ ├── advisory_cdn_metadata.json
│ └── advisory_cdn_filelist.json
└── RHSA-2020:0510/
├── advisory_cdn_metadata.json
└── advisory_cdn_filelist.json

File format
...........

**advisory_cdn_metadata.json**

This file contains advisory metadata in the standard Errata Tool format, including:

* Advisory ID, title, description, severity
* Package list with checksums
* References (CVEs, Bugzilla links, etc.)
* Release information

**advisory_cdn_filelist.json**

This file contains build and RPM information::

{
"build-nvr": {
"rpms": {
"rpm-filename.rpm": ["repo1", "repo2", ...]
},
"checksums": {
"md5": {
"rpm-filename.rpm": "checksum-value"
},
"sha256": {
"rpm-filename.rpm": "checksum-value"
}
},
"sig_key": "signing-key-id"
}
}

Differences from `ErrataSource`
...............................

Unlike the `ErrataSource`, the `KonfluxSource`:

* Reads from local JSON files rather than querying the Errata API
* Does not require Koji integration
* Does not currently support filtering by architecture (this use case may be supported in the future)
* Currently produces RPMs and advisories (additional content types such as modules and container images can be supported in the future)
* RPM push items have ``src=None`` (no local RPM files, only metadata)

Python API reference
--------------------

.. autoclass:: pushsource.KonfluxSource
:members:
:special-members: __init__
3 changes: 3 additions & 0 deletions docs/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ For detailed information, see the API reference of the associated class.
| errata | ``errata:https://errata.example.com?errata=RHBA-2020:1234`` | :class:`~pushsource.ErrataSource` | Obtain RPMs, container images and advisory |
| | | | metadata from Errata Tool |
+--------------+-----------------------------------------------------------------------------+-------------------------------------+----------------------------------------------------+
| konflux | ``konflux:/path/to/data?advisories=RHSA-2020:0509`` | :class:`~pushsource.KonfluxSource` | Obtain RPMs and advisory metadata from local |
| | | | JSON files organized by advisory |
+--------------+-----------------------------------------------------------------------------+-------------------------------------+----------------------------------------------------+
| file | ``file:/tmp/file-to-push`` | n/a | Obtain a single file-backed item of various types. |
+--------------+-----------------------------------------------------------------------------+ | |
| cgw | ``cgw:/tmp/yaml-file-to-push`` | | |
Expand Down
1 change: 1 addition & 0 deletions src/pushsource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from pushsource._impl.backend import (
ErrataSource,
KojiSource,
KonfluxSource,
StagedSource,
RegistrySource,
PubSource,
Expand Down
1 change: 1 addition & 0 deletions src/pushsource/_impl/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .errata_source import ErrataSource
from .koji_source import KojiSource
from .konflux_source import KonfluxSource
from .staged import StagedSource
from .registry_source import RegistrySource
from .direct import DirectSource
Expand Down
3 changes: 3 additions & 0 deletions src/pushsource/_impl/backend/konflux_source/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .konflux_source import KonfluxSource

__all__ = ["KonfluxSource"]
85 changes: 85 additions & 0 deletions src/pushsource/_impl/backend/konflux_source/konflux_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import json
import logging
import os

from ...compat_attr import attr

LOG = logging.getLogger("pushsource.konflux")


@attr.s()
class KonfluxAdvisoryData(object):
"""Stores advisory data loaded from JSON files."""

advisory_id = attr.ib(type=str)
metadata = attr.ib(type=dict) # advisory_cdn_metadata content
filelist = attr.ib(type=dict) # advisory_cdn_filelist content


class KonfluxLoader(object):
"""Loads advisory data from local JSON files."""

def __init__(self, base_dir):
"""Initialize loader with base directory.

Parameters:
base_dir (str):
Base directory containing advisory subdirectories.
"""
self._base_dir = base_dir

def load_advisory_data(self, advisory_id):
"""Load both advisory_cdn_metadata and advisory_cdn_filelist.

Parameters:
advisory_id (str):
Advisory ID (e.g., "RHSA-2020-0509")

Returns:
KonfluxAdvisoryData: Named tuple with metadata and filelist

Raises:
FileNotFoundError: If JSON files don't exist
ValueError: If JSON is invalid or malformed
"""
advisory_dir = os.path.join(self._base_dir, advisory_id)

LOG.debug("Loading advisory data for %s from %s", advisory_id, advisory_dir)

metadata = self._load_json(
os.path.join(advisory_dir, "advisory_cdn_metadata.json")
)
filelist = self._load_json(
os.path.join(advisory_dir, "advisory_cdn_filelist.json")
)

LOG.info("Successfully loaded advisory data for %s", advisory_id)

return KonfluxAdvisoryData(
advisory_id=advisory_id, metadata=metadata, filelist=filelist
)

def _load_json(self, filepath):
"""Load and parse a JSON file with error handling.

Parameters:
filepath (str):
Path to JSON file to load

Returns:
dict: Parsed JSON data

Raises:
FileNotFoundError: If file doesn't exist
ValueError: If JSON is invalid
"""
if not os.path.exists(filepath):
raise FileNotFoundError("Required file not found: %s" % filepath)

LOG.debug("Loading JSON file: %s", filepath)

with open(filepath, "r") as f:
try:
return json.load(f)
except json.JSONDecodeError as e:
raise ValueError("Invalid JSON in %s: %s" % (filepath, str(e))) from e
Loading