Skip to content

Commit a77e249

Browse files
yashvardhannanavatiJAVGan
authored andcommitted
Fix: Use unique sub-dir for extracting each FBC fragment
IIB used a config value to name the sub-directory when extracting the FBC fragment. This worked fine until we allowed one fragment per request. With the support for multiple fragments data gets overwritten causing unexpected behavior. This commit fixes that behavior Signed-off-by: Yashvardhan Nanavati <[email protected]> Assisted-by: Cursor Signed-off-by: Yashvardhan Nanavati <[email protected]>
1 parent 5d1121d commit a77e249

File tree

5 files changed

+88
-15
lines changed

5 files changed

+88
-15
lines changed

iib/workers/tasks/build_fbc_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,6 @@ def handle_fbc_operation_request(
150150
set_request_state(
151151
request_id,
152152
'complete',
153-
f"The {len(resolved_fbc_fragments)} FBC fragment(s) were successfully added"
153+
f"The {len(resolved_fbc_fragments)} FBC fragment(s) were successfully added "
154154
"in the index image",
155155
)

iib/workers/tasks/fbc_utils.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,27 @@ def merge_catalogs_dirs(src_config: str, dest_config: str):
102102
opm_validate(conf_dir)
103103

104104

105-
def extract_fbc_fragment(temp_dir: str, fbc_fragment: str) -> Tuple[str, List[str]]:
105+
def extract_fbc_fragment(
106+
temp_dir: str, fbc_fragment: str, fragment_index: int = 0
107+
) -> Tuple[str, List[str]]:
106108
"""
107109
Extract operator packages from the fbc_fragment image.
108110
109111
:param str temp_dir: base temp directory for IIB request.
110112
:param str fbc_fragment: pull specification of fbc_fragment in the IIB request.
113+
:param int fragment_index: index of the fragment to create unique paths and
114+
prevent cross-contamination.
111115
:return: fbc_fragment path, fbc_operator_packages.
112116
:rtype: tuple
113117
"""
114118
from iib.workers.tasks.build import _copy_files_from_image
115119

116120
log.info("Extracting the fbc_fragment's catalog from %s", fbc_fragment)
117-
# store the fbc_fragment at /tmp/iib-**/fbc-fragment
121+
# store the fbc_fragment at /tmp/iib-**/fbc-fragment-{index} to prevent
122+
# cross-contamination
118123
conf = get_worker_config()
119-
fbc_fragment_path = os.path.join(temp_dir, conf['temp_fbc_fragment_path'])
120-
# Copy fbc_fragment's catalog to /tmp/iib-**/fbc-fragment
124+
fbc_fragment_path = os.path.join(temp_dir, f"{conf['temp_fbc_fragment_path']}-{fragment_index}")
125+
# Copy fbc_fragment's catalog to /tmp/iib-**/fbc-fragment-{index}
121126
_copy_files_from_image(fbc_fragment, conf['fbc_fragment_catalog_path'], fbc_fragment_path)
122127

123128
log.info("fbc_fragment extracted at %s", fbc_fragment_path)

iib/workers/tasks/opm_operations.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,10 +1020,10 @@ def opm_registry_add_fbc_fragment(
10201020
fragment_data = []
10211021
all_fragment_operators = []
10221022

1023-
for fbc_fragment in fbc_fragments:
1023+
for i, fbc_fragment in enumerate(fbc_fragments):
10241024
# fragment path will look like /tmp/iib-**/fbc-fragment-{index}
10251025
fragment_path, fragment_operators = extract_fbc_fragment(
1026-
temp_dir=temp_dir, fbc_fragment=fbc_fragment
1026+
temp_dir=temp_dir, fbc_fragment=fbc_fragment, fragment_index=i
10271027
)
10281028
fragment_data.append((fragment_path, fragment_operators))
10291029
all_fragment_operators.extend(fragment_operators)
@@ -1066,7 +1066,8 @@ def opm_registry_add_fbc_fragment(
10661066
set_request_state(
10671067
request_id,
10681068
'in_progress',
1069-
f'Adding fbc fragment {i + 1}/{len(fbc_fragments)} to from_index',
1069+
f'Adding package(s) {fragment_operators} from fbc fragment '
1070+
f'{i + 1}/{len(fbc_fragments)} to from_index',
10701071
)
10711072

10721073
for fragment_operator in fragment_operators:

tests/test_workers/test_tasks/test_fbc_utils.py

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ def test_enforce_json_config_dir_multiple_chunks_input(tmpdir):
196196
'{"one_more": "chunk", "createdAt": "2025-01-21T07:15:29"}'
197197
)
198198

199-
input = os.path.join(tmpdir, f"test_file.yaml")
200-
output = os.path.join(tmpdir, f"test_file.json")
199+
input = os.path.join(tmpdir, "test_file.yaml")
200+
output = os.path.join(tmpdir, "test_file.json")
201201
with open(input, 'w') as w:
202202
w.write(dedent(multiple_chunks_yaml))
203203

@@ -213,15 +213,82 @@ def test_enforce_json_config_dir_multiple_chunks_input(tmpdir):
213213
def test_extract_fbc_fragment(mock_cffi, mock_osldr, ldr_output, tmpdir):
214214
test_fbc_fragment = "example.com/test/fbc_fragment:latest"
215215
mock_osldr.return_value = ldr_output
216-
fbc_fragment_path = os.path.join(tmpdir, get_worker_config()['temp_fbc_fragment_path'])
216+
# The function now adds -0 suffix by default when fragment_index is not provided
217+
fbc_fragment_path = os.path.join(tmpdir, f"{get_worker_config()['temp_fbc_fragment_path']}-0")
217218

218219
if not ldr_output:
219220
with pytest.raises(IIBError):
220221
extract_fbc_fragment(tmpdir, test_fbc_fragment)
221222
else:
222223
extract_fbc_fragment(tmpdir, test_fbc_fragment)
223-
mock_cffi.assert_has_calls([mock.call(test_fbc_fragment, '/configs', fbc_fragment_path)])
224-
mock_osldr.assert_has_calls([mock.call(fbc_fragment_path)])
224+
mock_cffi.assert_called_once_with(
225+
test_fbc_fragment, get_worker_config()['fbc_fragment_catalog_path'], fbc_fragment_path
226+
)
227+
mock_osldr.assert_called_once_with(fbc_fragment_path)
228+
229+
230+
@pytest.mark.parametrize('ldr_output', [['testoperator'], ['test1', 'test2']])
231+
@mock.patch('os.listdir')
232+
@mock.patch('iib.workers.tasks.build._copy_files_from_image')
233+
def test_extract_fbc_fragment_with_index(mock_cffi, mock_osldr, ldr_output, tmpdir):
234+
"""Test extract_fbc_fragment with non-zero fragment_index values."""
235+
test_fbc_fragment = "example.com/test/fbc_fragment:latest"
236+
mock_osldr.return_value = ldr_output
237+
238+
# Test with fragment_index = 2
239+
fragment_index = 2
240+
fbc_fragment_path = os.path.join(
241+
tmpdir, f"{get_worker_config()['temp_fbc_fragment_path']}-{fragment_index}"
242+
)
243+
244+
result_path, result_operators = extract_fbc_fragment(
245+
tmpdir, test_fbc_fragment, fragment_index=fragment_index
246+
)
247+
248+
# Verify the path includes the correct index
249+
assert result_path == fbc_fragment_path
250+
assert result_path.endswith(f"-{fragment_index}")
251+
assert result_operators == ldr_output
252+
253+
# Verify the function was called with the correct path
254+
mock_cffi.assert_called_once_with(
255+
test_fbc_fragment, get_worker_config()['fbc_fragment_catalog_path'], fbc_fragment_path
256+
)
257+
mock_osldr.assert_called_once_with(fbc_fragment_path)
258+
259+
260+
@mock.patch('os.listdir')
261+
@mock.patch('iib.workers.tasks.build._copy_files_from_image')
262+
def test_extract_fbc_fragment_isolation(mock_cffi, mock_osldr, tmpdir):
263+
"""Test that multiple fragments with different indices create isolated directories."""
264+
test_fbc_fragment1 = "example.com/test/fbc_fragment1:latest"
265+
test_fbc_fragment2 = "example.com/test/fbc_fragment2:latest"
266+
267+
# Mock different outputs for each fragment
268+
mock_osldr.side_effect = [['operator1'], ['operator2']]
269+
270+
# Extract first fragment with index 0
271+
path1, operators1 = extract_fbc_fragment(tmpdir, test_fbc_fragment1, fragment_index=0)
272+
273+
# Extract second fragment with index 1
274+
path2, operators2 = extract_fbc_fragment(tmpdir, test_fbc_fragment2, fragment_index=1)
275+
276+
# Verify paths are different and include correct indices
277+
assert path1 != path2
278+
assert path1.endswith("-0")
279+
assert path2.endswith("-1")
280+
281+
# Verify operators are different (no cross-contamination)
282+
assert operators1 == ['operator1']
283+
assert operators2 == ['operator2']
284+
assert operators1 != operators2
285+
286+
# Verify _copy_files_from_image was called with different paths
287+
expected_calls = [
288+
mock.call(test_fbc_fragment1, get_worker_config()['fbc_fragment_catalog_path'], path1),
289+
mock.call(test_fbc_fragment2, get_worker_config()['fbc_fragment_catalog_path'], path2),
290+
]
291+
mock_cffi.assert_has_calls(expected_calls, any_order=True)
225292

226293

227294
def test__serialize_datetime():
@@ -231,5 +298,5 @@ def test__serialize_datetime():
231298

232299

233300
def test__serialize_datetime_raise():
234-
with pytest.raises(TypeError, match=f"Type <class 'int'> is not serializable."):
301+
with pytest.raises(TypeError, match="Type <class 'int'> is not serializable."):
235302
_serialize_datetime(2025)

tests/test_workers/test_tasks/test_opm_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ def test_opm_registry_add_fbc_fragment(
896896
10, tmpdir, from_index, binary_image, [fbc_fragment], None
897897
)
898898

899-
mock_eff.assert_called_with(temp_dir=tmpdir, fbc_fragment=fbc_fragment)
899+
mock_eff.assert_called_with(temp_dir=tmpdir, fbc_fragment=fbc_fragment, fragment_index=0)
900900
mock_voe.assert_called_with(
901901
from_index=from_index,
902902
base_dir=tmpdir,

0 commit comments

Comments
 (0)