Skip to content

Commit ad25d9b

Browse files
Fix ImageStream cache failure crashing requests instead of falling back to Quay
When iib_use_imagestream_cache is enabled but the index-db-cache ImageStream is unavailable, the IIBError from oc now triggers a graceful fallback to pulling index.db directly from Quay instead of failing the entire request. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent caa7e3b commit ad25d9b

2 files changed

Lines changed: 147 additions & 25 deletions

File tree

iib/workers/tasks/containerized_utils.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -242,33 +242,35 @@ def pull_index_db_artifact(from_index: str, temp_dir: str) -> str:
242242
"""
243243
conf = get_worker_config()
244244
if conf.get('iib_use_imagestream_cache', False):
245-
# Verify index.db cache is synced. Refresh if not.
246245
log.info('ImageStream cache is enabled. Checking cache sync status.')
247-
if verify_indexdb_cache_for_image(from_index):
248-
log.info('Index.db cache is synced. Pulling from ImageStream.')
249-
# Pull from ImageStream when digests match
250-
imagestream_ref = get_imagestream_artifact_pullspec(from_index)
251-
artifact_dir = get_oras_artifact(
252-
imagestream_ref,
253-
temp_dir,
254-
)
255-
else:
256-
log.info('Index.db cache is not synced. Refreshing and pulling from Quay.')
257-
refresh_indexdb_cache_for_image(from_index)
258-
# Pull directly from Quay after triggering refresh
259-
artifact_ref = get_indexdb_artifact_pullspec(from_index)
260-
artifact_dir = get_oras_artifact(
261-
artifact_ref,
262-
temp_dir,
263-
)
246+
# Only ImageStream-specific operations are inside the try/except.
247+
# If the ImageStream pull succeeds, return early. Otherwise fall
248+
# through to the Quay pull below so that Quay failures are never
249+
# masked as ImageStream cache errors.
250+
try:
251+
if verify_indexdb_cache_for_image(from_index):
252+
log.info('Index.db cache is synced. Pulling from ImageStream.')
253+
imagestream_ref = get_imagestream_artifact_pullspec(from_index)
254+
artifact_dir = get_oras_artifact(
255+
imagestream_ref,
256+
temp_dir,
257+
)
258+
return artifact_dir
259+
else:
260+
# Cache is stale — refresh it for future requests
261+
log.info('Index.db cache is not synced. Refreshing cache.')
262+
refresh_indexdb_cache_for_image(from_index)
263+
except IIBError as e:
264+
log.warning('ImageStream cache access failed, falling back to Quay: %s', e)
264265
else:
265-
# Pull directly from Quay without ImageStream cache
266266
log.info('ImageStream cache is disabled. Pulling index.db artifact directly from registry.')
267-
artifact_ref = get_indexdb_artifact_pullspec(from_index)
268-
artifact_dir = get_oras_artifact(
269-
artifact_ref,
270-
temp_dir,
271-
)
267+
268+
# Pull directly from Quay — either cache is disabled, stale, or unavailable
269+
artifact_ref = get_indexdb_artifact_pullspec(from_index)
270+
artifact_dir = get_oras_artifact(
271+
artifact_ref,
272+
temp_dir,
273+
)
272274

273275
return artifact_dir
274276

tests/test_workers/test_tasks/test_containerized_utils.py

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def test_pull_index_db_artifact_imagestream_enabled_cache_not_synced(
9494
mock_get_indexdb_artifact_pullspec.assert_called_once_with(from_index)
9595
mock_get_oras_artifact.assert_called_once_with(artifact_ref, temp_dir)
9696
mock_log.info.assert_any_call('ImageStream cache is enabled. Checking cache sync status.')
97-
mock_log.info.assert_any_call('Index.db cache is not synced. Refreshing and pulling from Quay.')
97+
mock_log.info.assert_any_call('Index.db cache is not synced. Refreshing cache.')
9898

9999

100100
@patch('iib.workers.tasks.containerized_utils.get_worker_config')
@@ -162,6 +162,126 @@ def test_pull_index_db_artifact_default_config_behaves_as_disabled(
162162
mock_get_oras_artifact.assert_called_once_with(artifact_ref, temp_dir)
163163

164164

165+
@patch('iib.workers.tasks.containerized_utils.get_worker_config')
166+
@patch('iib.workers.tasks.containerized_utils.log')
167+
@patch('iib.workers.tasks.containerized_utils.refresh_indexdb_cache_for_image')
168+
@patch('iib.workers.tasks.containerized_utils.verify_indexdb_cache_for_image')
169+
@patch('iib.workers.tasks.containerized_utils.get_indexdb_artifact_pullspec')
170+
@patch('iib.workers.tasks.containerized_utils.get_imagestream_artifact_pullspec')
171+
@patch('iib.workers.tasks.containerized_utils.get_oras_artifact')
172+
def test_pull_index_db_artifact_verify_cache_fails_falls_back_to_quay(
173+
mock_get_oras_artifact,
174+
mock_get_imagestream_artifact_pullspec,
175+
mock_get_indexdb_artifact_pullspec,
176+
mock_verify_cache,
177+
mock_refresh_cache,
178+
mock_log,
179+
mock_get_worker_config,
180+
):
181+
"""When ImageStream verify raises IIBError, fall back to Quay."""
182+
mock_get_worker_config.return_value = {'iib_use_imagestream_cache': True}
183+
error = IIBError('imagestreams.image.openshift.io not found')
184+
mock_verify_cache.side_effect = error
185+
186+
from_index = 'quay.io/ns/index-image@sha256:abc'
187+
temp_dir = '/tmp/some-dir'
188+
artifact_ref = 'quay.io/ns/index-image-indexdb:v4.19'
189+
artifact_dir = '/tmp/artifact-dir'
190+
191+
mock_get_indexdb_artifact_pullspec.return_value = artifact_ref
192+
mock_get_oras_artifact.return_value = artifact_dir
193+
194+
result = pull_index_db_artifact(from_index, temp_dir)
195+
196+
assert result == artifact_dir
197+
mock_verify_cache.assert_called_once_with(from_index)
198+
mock_get_indexdb_artifact_pullspec.assert_called_once_with(from_index)
199+
mock_get_oras_artifact.assert_called_once_with(artifact_ref, temp_dir)
200+
mock_log.warning.assert_called_once_with(
201+
'ImageStream cache access failed, falling back to Quay: %s', error
202+
)
203+
204+
205+
@patch('iib.workers.tasks.containerized_utils.get_worker_config')
206+
@patch('iib.workers.tasks.containerized_utils.log')
207+
@patch('iib.workers.tasks.containerized_utils.refresh_indexdb_cache_for_image')
208+
@patch('iib.workers.tasks.containerized_utils.verify_indexdb_cache_for_image')
209+
@patch('iib.workers.tasks.containerized_utils.get_indexdb_artifact_pullspec')
210+
@patch('iib.workers.tasks.containerized_utils.get_imagestream_artifact_pullspec')
211+
@patch('iib.workers.tasks.containerized_utils.get_oras_artifact')
212+
def test_pull_index_db_artifact_imagestream_pull_fails_falls_back_to_quay(
213+
mock_get_oras_artifact,
214+
mock_get_imagestream_artifact_pullspec,
215+
mock_get_indexdb_artifact_pullspec,
216+
mock_verify_cache,
217+
mock_refresh_cache,
218+
mock_log,
219+
mock_get_worker_config,
220+
):
221+
"""When ImageStream pull raises IIBError, fall back to Quay."""
222+
mock_get_worker_config.return_value = {'iib_use_imagestream_cache': True}
223+
mock_verify_cache.return_value = True
224+
mock_get_imagestream_artifact_pullspec.return_value = 'imagestream-ref'
225+
error = IIBError('Failed to pull from ImageStream registry')
226+
artifact_ref = 'quay.io/ns/index-image-indexdb:v4.19'
227+
artifact_dir = '/tmp/artifact-dir'
228+
mock_get_oras_artifact.side_effect = [error, artifact_dir]
229+
mock_get_indexdb_artifact_pullspec.return_value = artifact_ref
230+
231+
from_index = 'quay.io/ns/index-image@sha256:abc'
232+
temp_dir = '/tmp/some-dir'
233+
234+
result = pull_index_db_artifact(from_index, temp_dir)
235+
236+
assert result == artifact_dir
237+
mock_get_oras_artifact.assert_any_call('imagestream-ref', temp_dir)
238+
mock_get_oras_artifact.assert_any_call(artifact_ref, temp_dir)
239+
mock_log.warning.assert_called_once_with(
240+
'ImageStream cache access failed, falling back to Quay: %s', error
241+
)
242+
243+
244+
@patch('iib.workers.tasks.containerized_utils.get_worker_config')
245+
@patch('iib.workers.tasks.containerized_utils.log')
246+
@patch('iib.workers.tasks.containerized_utils.refresh_indexdb_cache_for_image')
247+
@patch('iib.workers.tasks.containerized_utils.verify_indexdb_cache_for_image')
248+
@patch('iib.workers.tasks.containerized_utils.get_indexdb_artifact_pullspec')
249+
@patch('iib.workers.tasks.containerized_utils.get_imagestream_artifact_pullspec')
250+
@patch('iib.workers.tasks.containerized_utils.get_oras_artifact')
251+
def test_pull_index_db_artifact_refresh_cache_fails_falls_back_to_quay(
252+
mock_get_oras_artifact,
253+
mock_get_imagestream_artifact_pullspec,
254+
mock_get_indexdb_artifact_pullspec,
255+
mock_verify_cache,
256+
mock_refresh_cache,
257+
mock_log,
258+
mock_get_worker_config,
259+
):
260+
"""When cache refresh raises IIBError, fall back to Quay."""
261+
mock_get_worker_config.return_value = {'iib_use_imagestream_cache': True}
262+
mock_verify_cache.return_value = False
263+
error = IIBError('oc import-image failed')
264+
mock_refresh_cache.side_effect = error
265+
266+
from_index = 'quay.io/ns/index-image@sha256:abc'
267+
temp_dir = '/tmp/some-dir'
268+
artifact_ref = 'quay.io/ns/index-image-indexdb:v4.19'
269+
artifact_dir = '/tmp/artifact-dir'
270+
271+
mock_get_indexdb_artifact_pullspec.return_value = artifact_ref
272+
mock_get_oras_artifact.return_value = artifact_dir
273+
274+
result = pull_index_db_artifact(from_index, temp_dir)
275+
276+
assert result == artifact_dir
277+
mock_refresh_cache.assert_called_once_with(from_index)
278+
mock_get_indexdb_artifact_pullspec.assert_called_once_with(from_index)
279+
mock_get_oras_artifact.assert_called_once_with(artifact_ref, temp_dir)
280+
mock_log.warning.assert_called_once_with(
281+
'ImageStream cache access failed, falling back to Quay: %s', error
282+
)
283+
284+
165285
@patch('iib.workers.tasks.containerized_utils.log')
166286
def test_write_build_metadata_creates_expected_json(mock_log, tmp_path):
167287
"""write_build_metadata should create JSON file with expected content."""

0 commit comments

Comments
 (0)