-
Notifications
You must be signed in to change notification settings - Fork 25
Adding helper functions for index.db caching #1162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ | |
|
||
from iib.common.tracing import instrument_tracing | ||
from iib.exceptions import IIBError | ||
from iib.workers.tasks.utils import run_cmd, set_registry_auths | ||
from iib.workers.tasks.utils import run_cmd, set_registry_auths, get_image_digest | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
@@ -102,3 +102,75 @@ def push_oras_artifact( | |
log.info('Successfully pushed OCI artifact to %s', artifact_ref) | ||
except Exception as e: | ||
raise IIBError(f'Failed to push OCI artifact to {artifact_ref}: {e}') | ||
|
||
|
||
def get_image_stream_digest( | ||
tag: str, | ||
): | ||
""" | ||
Retrieve the image digest from the OpenShift ImageStream. | ||
|
||
This function queries the `index-db-cache` ImageStream to get the | ||
SHA256 digest for a specific tag. | ||
|
||
:param tag: The image tag to check. | ||
:return: The image digest (e.g., "sha256:..."). | ||
:rtype: str | ||
""" | ||
jsonpath = f'\'{{.status.tags[?(@.tag=="{tag}")].items[0].image}}\'' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth adding a comment here for posterity |
||
return run_cmd( | ||
['oc', 'get', 'imagestream', 'index-db-cache', '-o', f'jsonpath={jsonpath}'], | ||
exc_msg=f'Failed to get digest for ImageStream tag {tag}.', | ||
) | ||
|
||
|
||
def verify_indexdb_cache_sync(tag: str) -> bool: | ||
""" | ||
Compare the digest of the ImageStream with the digest of the image in repository. | ||
|
||
This function verifies if the local ImageStream cache is up to date with | ||
the latest image in the remote registry. | ||
|
||
:param tag: The image tag to verify. | ||
:return: True if the digests match (cache is synced), False otherwise. | ||
:rtype: bool | ||
""" | ||
# TODO - This can be loaded from config variable | ||
repository = "quay.io/my-org/index-db" | ||
|
||
quay_digest = get_image_digest(f"{repository}:{tag}") | ||
is_digest = get_image_stream_digest(tag) | ||
|
||
return quay_digest == is_digest | ||
|
||
|
||
def refresh_indexdb_cache( | ||
tag: str, | ||
registry_auths: Optional[Dict[str, Any]] = None, | ||
) -> None: | ||
""" | ||
Force a synchronization of the ImageStream with the remote registry. | ||
|
||
This function imports the specified image from Quay.io into the `index-db-cache` | ||
ImageStream, ensuring the local cache is up-to-date. | ||
|
||
:param tag: The container image tag to refresh. | ||
:param registry_auths: Optional authentication data for the registry. | ||
""" | ||
log.info('Refreshing OCI artifact cache: %s', tag) | ||
|
||
# TODO - This can be loaded from config variable | ||
repository = "quay.io/my-org/index-db" | ||
|
||
# Use namespace-specific registry authentication if provided | ||
with set_registry_auths(registry_auths, use_empty_config=True): | ||
run_cmd( | ||
[ | ||
'oc', | ||
'import-image', | ||
f'index-db-cache:{tag}', | ||
f'--from={repository}:{tag}', | ||
'--confirm', | ||
], | ||
exc_msg=f'Failed to refresh OCI artifact {tag}.', | ||
) |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,6 +8,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from iib.workers.tasks.oras_utils import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get_oras_artifact, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
push_oras_artifact, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
verify_indexdb_cache_sync, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get_image_stream_digest, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
refresh_indexdb_cache, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -326,3 +329,92 @@ def test_get_oras_artifact_with_base_dir_wont_leak_credentials( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
all_messages = ' '.join(caplog.messages) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert 'dXNlcjpwYXNz' not in all_messages # base64 encoded credentials | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert 'user:pass' not in all_messages # decoded credentials | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.run_cmd') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def test_get_image_stream_digest(mock_run_cmd): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+334
to
+335
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Consider testing get_image_stream_digest with unexpected output formats. Please add tests for cases where run_cmd returns values like an empty string, None, or a non-digest value to verify error handling in get_image_stream_digest. Suggested implementation: @mock.patch('iib.workers.tasks.oras_utils.run_cmd')
def test_get_image_stream_digest(mock_run_cmd):
"""Test successful retrieval of image digest from ImageStream."""
mock_run_cmd.return_value = 'sha256:12345'
tag = 'test-tag'
digest = get_image_stream_digest(tag)
assert digest == 'sha256:12345'
mock_run_cmd.assert_called_once_with(
[
'oc',
'get',
'imagestream',
]
)
@mock.patch('iib.workers.tasks.oras_utils.run_cmd')
def test_get_image_stream_digest_empty_string(mock_run_cmd):
"""Test get_image_stream_digest with empty string output."""
mock_run_cmd.return_value = ''
tag = 'test-tag'
try:
digest = get_image_stream_digest(tag)
except Exception as exc:
assert isinstance(exc, Exception)
else:
assert digest is None or digest == '', "Expected None or empty digest for empty string output"
@mock.patch('iib.workers.tasks.oras_utils.run_cmd')
def test_get_image_stream_digest_none(mock_run_cmd):
"""Test get_image_stream_digest with None output."""
mock_run_cmd.return_value = None
tag = 'test-tag'
try:
digest = get_image_stream_digest(tag)
except Exception as exc:
assert isinstance(exc, Exception)
else:
assert digest is None, "Expected None digest for None output"
@mock.patch('iib.workers.tasks.oras_utils.run_cmd')
def test_get_image_stream_digest_invalid_format(mock_run_cmd):
"""Test get_image_stream_digest with non-digest output."""
mock_run_cmd.return_value = 'not-a-digest'
tag = 'test-tag'
try:
digest = get_image_stream_digest(tag)
except Exception as exc:
assert isinstance(exc, Exception)
else:
assert digest is None or digest == 'not-a-digest', "Expected None or raw output for invalid digest format" You may need to adjust the assertions in the new tests depending on how |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Test successful retrieval of image digest from ImageStream.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_run_cmd.return_value = 'sha256:12345' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tag = 'test-tag' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
digest = get_image_stream_digest(tag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert digest == 'sha256:12345' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_run_cmd.assert_called_once_with( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'oc', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'get', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'imagestream', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'index-db-cache', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'-o', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'jsonpath=\'{.status.tags[?(@.tag=="test-tag")].items[0].image}\'', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
exc_msg='Failed to get digest for ImageStream tag test-tag.', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.run_cmd', side_effect=IIBError('cmd failed')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def test_get_image_stream_digest_failure(mock_run_cmd): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Test failure during retrieval of image digest from ImageStream.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
with pytest.raises(IIBError, match='cmd failed'): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get_image_stream_digest('test-tag') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.get_image_stream_digest') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.get_image_digest') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def test_verify_indexdb_cache_sync_match(mock_get_image_digest, mock_get_is_digest): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+363
to
+365
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Consider adding a test for verify_indexdb_cache_sync when get_image_digest or get_image_stream_digest raises an exception. Please add a test to verify that verify_indexdb_cache_sync properly handles exceptions from get_image_digest or get_image_stream_digest. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Test successful verification when digests match.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_image_digest.return_value = 'sha256:abc' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_is_digest.return_value = 'sha256:abc' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tag = 'test-tag' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = verify_indexdb_cache_sync(tag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert result is True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_image_digest.assert_called_once_with('quay.io/my-org/index-db:test-tag') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_is_digest.assert_called_once_with(tag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.get_image_stream_digest') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.get_image_digest') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def test_verify_indexdb_cache_sync_no_match(mock_get_image_digest, mock_get_is_digest): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Test successful verification when digests don't match.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_image_digest.return_value = 'sha256:abc' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_is_digest.return_value = 'sha256:xyz' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tag = 'test-tag' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = verify_indexdb_cache_sync(tag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert result is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_image_digest.assert_called_once_with('quay.io/my-org/index-db:test-tag') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_get_is_digest.assert_called_once_with(tag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.set_registry_auths') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.run_cmd') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def test_refresh_indexdb_cache_success(mock_run_cmd, mock_auth, registry_auths): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Test successful cache refresh.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tag = 'test-tag' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+393
to
+397
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Consider testing refresh_indexdb_cache with missing or invalid registry_auths. Please add tests for cases where registry_auths is None or contains invalid data to verify correct handling by refresh_indexdb_cache.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
refresh_indexdb_cache(tag, registry_auths) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_auth.assert_called_once_with(registry_auths, use_empty_config=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mock_run_cmd.assert_called_once_with( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'oc', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'import-image', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'index-db-cache:test-tag', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'--from=quay.io/my-org/index-db:test-tag', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'--confirm', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
exc_msg='Failed to refresh OCI artifact test-tag.', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@mock.patch('iib.workers.tasks.oras_utils.run_cmd', side_effect=IIBError('refresh failed')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def test_refresh_indexdb_cache_failure(mock_run_cmd): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Test cache refresh failure.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tag = 'test-tag' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
with pytest.raises(IIBError, match='refresh failed'): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
refresh_indexdb_cache(tag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: The jsonpath string uses single quotes which may cause issues with oc CLI.
Consider removing the single quotes from the jsonpath expression unless they are specifically required, as they may cause parsing issues with run_cmd.