Skip to content

Commit

Permalink
cachi2: update add_image_content_manifest plugin
Browse files Browse the repository at this point in the history
Update add_image_content_manifest to process cachi2 results.

ICM must be generated from cachi2 SBOM in this case.

Signed-off-by: Martin Basti <mbasti@redhat.com>
  • Loading branch information
MartinBasti committed Nov 6, 2024
1 parent 4ca7398 commit 7b2ce79
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 4 deletions.
13 changes: 13 additions & 0 deletions atomic_reactor/plugins/add_image_content_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@

from atomic_reactor.constants import (IMAGE_BUILD_INFO_DIR, INSPECT_ROOTFS,
INSPECT_ROOTFS_LAYERS,
CACHI2_BUILD_DIR,
PLUGIN_ADD_IMAGE_CONTENT_MANIFEST,
PLUGIN_FETCH_MAVEN_KEY,
PLUGIN_CACHI2_POSTPROCESS,
PLUGIN_RESOLVE_REMOTE_SOURCE)
from atomic_reactor.config import get_cachito_session
from atomic_reactor.dirs import BuildDir
from atomic_reactor.plugin import Plugin
from atomic_reactor.util import (validate_with_schema, read_content_sets, map_to_user_params,
allow_path_in_dockerignore)
from atomic_reactor.utils.pnc import PNCUtil
from atomic_reactor.utils.cachi2 import convert_SBOM_to_ICM


class AddImageContentManifestPlugin(Plugin):
Expand Down Expand Up @@ -100,6 +103,8 @@ def __init__(self, workflow, destdir=IMAGE_BUILD_INFO_DIR):
remote_source_results = wf_data.plugins_results.get(PLUGIN_RESOLVE_REMOTE_SOURCE) or []
self.remote_source_ids = [remote_source['id'] for remote_source in remote_source_results]

self.cachi2_remote_sources = wf_data.plugins_results.get(PLUGIN_CACHI2_POSTPROCESS) or []

fetch_maven_results = wf_data.plugins_results.get(PLUGIN_FETCH_MAVEN_KEY) or {}
self.pnc_artifact_ids = fetch_maven_results.get('pnc_artifact_ids') or []

Expand Down Expand Up @@ -130,6 +135,12 @@ def layer_index(self) -> int:

return len(inspect[INSPECT_ROOTFS][INSPECT_ROOTFS_LAYERS])

def _get_cachi2_icm(self) -> dict:
global_sbom_path = self.workflow.build_dir.path/CACHI2_BUILD_DIR/"bom.json"
with open(global_sbom_path, "r") as f:
sbom = json.load(f)
return convert_SBOM_to_ICM(sbom)

@functools.cached_property
def _icm_base(self) -> dict:
"""Create the platform-independent skeleton of the ICM document.
Expand All @@ -140,6 +151,8 @@ def _icm_base(self) -> dict:

if self.remote_source_ids:
icm = self.cachito_session.get_image_content_manifest(self.remote_source_ids)
elif self.cachi2_remote_sources: # we doesn't support Cachito and Cachi2 together
icm = self._get_cachi2_icm()

if self.pnc_artifact_ids:
purl_specs = self.pnc_util.get_artifact_purl_specs(self.pnc_artifact_ids)
Expand Down
74 changes: 70 additions & 4 deletions tests/plugins/test_add_image_content_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
from tests.utils.test_cachito import CACHITO_URL, CACHITO_REQUEST_ID

from atomic_reactor.constants import (
CACHI2_BUILD_DIR,
INSPECT_ROOTFS,
INSPECT_ROOTFS_LAYERS,
PLUGIN_CACHI2_POSTPROCESS,
PLUGIN_FETCH_MAVEN_KEY,
PLUGIN_RESOLVE_REMOTE_SOURCE,
DOCKERIGNORE,
Expand All @@ -39,6 +41,28 @@
}
CACHITO_ICM_URL = '{}/api/v1/content-manifest?requests={}'.format(CACHITO_URL,
CACHITO_REQUEST_ID)

CACHI2_SBOM = {
"bomFormat": "CycloneDX",
"components": [{
"name": "retrodep",
"purl": "pkg:golang/github.com%2Frelease-engineering%2Fretrodep%2Fv2@v2.0.2",
"properties": [{
"name": "cachi2:found_by",
"value": "cachi2",
}],
"type": "library",
}],
"metadata": {
"tools": [{
"vendor": "red hat",
"name": "cachi2"
}]
},
"specVersion": "1.4",
"version": 1
}

PNC_ARTIFACT = {
'id': 1234,
'publicUrl': 'http://test.com/artifact.jar',
Expand Down Expand Up @@ -88,6 +112,28 @@
}
]
}

CACHI2_ICM_DICT = {
'metadata': {
'icm_version': 1,
'icm_spec': (
'https://raw.githubusercontent.com/containerbuildsystem/atomic-reactor/'
'f4abcfdaf8247a6b074f94fa84f3846f82d781c6/atomic_reactor/schemas/'
'content_manifest.json'),
'image_layer_index': 1,
},
'content_sets': [],
'image_contents': [
{
'purl':
'pkg:golang/github.com%2Frelease-engineering%2Fretrodep%2Fv2@v2.0.2',
},
{
'purl': PNC_ARTIFACT['purl'],
}
]
}

ICM_JSON = dedent(
'''\
{
Expand Down Expand Up @@ -157,7 +203,8 @@ def mock_get_icm(requests_mock):


def mock_env(workflow, df_content, base_layers=0, remote_sources=None,
r_c_m_override=None, pnc_artifacts=True, dockerignore=False):
r_c_m_override=None, pnc_artifacts=True, dockerignore=False,
cachi2_sbom=None):

if base_layers > 0:
inspection_data = {
Expand Down Expand Up @@ -206,6 +253,19 @@ def mock_env(workflow, df_content, base_layers=0, remote_sources=None,
platforms = list(CONTENT_SETS.keys())
workflow.build_dir.init_build_dirs(platforms, workflow.source)

if cachi2_sbom:
env.set_plugin_result(
PLUGIN_CACHI2_POSTPROCESS,
{"plugin": "did run, real value doesn't matter"}
)

# save cachi2 SBOM which is source for ICM
path = workflow.build_dir.path/CACHI2_BUILD_DIR/"bom.json"
path.parent.mkdir()
with open(path, "w") as f:
json.dump(cachi2_sbom, f)
f.flush()

return env.create_runner()


Expand Down Expand Up @@ -239,6 +299,7 @@ def check_in_build_dir(build_dir):

@pytest.mark.parametrize('manifest_file_exists', [True, False])
@pytest.mark.parametrize('content_sets', [True, False])
@pytest.mark.parametrize('cachi2', [True, False])
@pytest.mark.parametrize(
('df_content, expected_df, base_layers, manifest_file'), [
(
Expand Down Expand Up @@ -288,13 +349,18 @@ def check_in_build_dir(build_dir):
),
])
def test_add_image_content_manifest(workflow, requests_mock,
manifest_file_exists, content_sets,
manifest_file_exists, content_sets, cachi2,
df_content, expected_df, base_layers, manifest_file,
):
mock_get_icm(requests_mock)
mock_content_sets_config(workflow.source.path, empty=(not content_sets))

runner = mock_env(workflow, df_content, base_layers, remote_sources=REMOTE_SOURCES)
if cachi2:
runner_opts = {"cachi2_sbom": CACHI2_SBOM}
else:
runner_opts = {"remote_sources": REMOTE_SOURCES}

runner = mock_env(workflow, df_content, base_layers, **runner_opts)

if manifest_file_exists:
workflow.build_dir.any_platform.path.joinpath(manifest_file).touch()
Expand All @@ -304,7 +370,7 @@ def test_add_image_content_manifest(workflow, requests_mock,
runner.run()
return

expected_output = deepcopy(ICM_DICT)
expected_output = deepcopy(CACHI2_ICM_DICT if cachi2 else ICM_DICT)
expected_output['metadata']['image_layer_index'] = base_layers if base_layers else 0

runner.run()
Expand Down

0 comments on commit 7b2ce79

Please sign in to comment.