-
Notifications
You must be signed in to change notification settings - Fork 25
Draft: Add handler for containerized RM request #1235
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: main
Are you sure you want to change the base?
Draft: Add handler for containerized RM request #1235
Conversation
Signed-off-by: Yashvardhan Nanavati <[email protected]> Assisted-by: Cursor Signed-off-by: Yashvardhan Nanavati <[email protected]>
Reviewer's GuideThis PR introduces a new containerized remove-operators request handler that coordinates Git-based catalog updates and Konflux pipelines, enhances existing Konflux utilities with retry and image URL extraction logic, extends ORAS artifact tooling with working-directory support, and augments configuration with index.db cache settings. Sequence diagram for containerized remove-operators request workflowsequenceDiagram
actor User
participant "IIB Worker"
participant "Git Repository"
participant "Konflux Pipeline"
participant "Registry"
User->>"IIB Worker": Submit remove-operators request
"IIB Worker"->>"Git Repository": Clone repo, remove operators, commit/push
"IIB Worker"->>"Konflux Pipeline": Trigger pipeline via commit
"IIB Worker"->>"Konflux Pipeline": Wait for pipeline completion
"Konflux Pipeline"-->>"IIB Worker": Pipeline completes, provides IMAGE_URL
"IIB Worker"->>"Registry": Copy built index image to output registry
"IIB Worker"->>User: Notify completion
ER diagram for new and updated configuration fields for index.db cacheerDiagram
CONFIG {
string iib_index_db_artifact_registry
string iib_index_db_imagestream_registry
string iib_index_db_artifact_tag_template
string iib_index_db_artifact_template
boolean iib_use_imagestream_cache
}
CONFIG ||--o| "ORAS_ARTIFACT" : uses
CONFIG ||--o| "IMAGESTREAM_CACHE" : configures
Class diagram for new containerized remove-operators handler and related utilitiesclassDiagram
class build_containerized_rm {
+handle_containerized_rm_request(operators, request_id, from_index, ...)
}
class containerized_utils {
+pull_index_db_artifact(from_index, temp_dir)
+write_build_metadata(local_repo_path, opm_version, ocp_version, distribution_scope, binary_image, request_id)
+cleanup_on_failure(mr_details, last_commit_sha, index_git_repo, overwrite_from_index, request_id, from_index, index_repo_map, original_index_db_digest, reason)
}
class konflux_utils {
+find_pipelinerun(commit_sha)
+wait_for_pipeline_completion(pipelinerun_name, timeout)
+get_pipelinerun_image_url(pipelinerun_name, run)
}
class oras_utils {
+push_oras_artifact(artifact_ref, local_path, artifact_type, registry_auths, annotations, cwd)
+refresh_indexdb_cache_for_image(index_image_pullspec)
+get_imagestream_artifact_pullspec(from_index)
}
class Config {
+iib_index_db_artifact_registry: str
+iib_index_db_imagestream_registry: str
+iib_index_db_artifact_tag_template: str
+iib_index_db_artifact_template: str
+iib_use_imagestream_cache: bool
}
build_containerized_rm --> containerized_utils
build_containerized_rm --> konflux_utils
build_containerized_rm --> oras_utils
build_containerized_rm --> Config
containerized_utils --> oras_utils
containerized_utils --> Config
konflux_utils --> Config
oras_utils --> Config
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
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.
Hey there - I've reviewed your changes - here's some feedback:
- The handle_containerized_rm_request task is over 400 lines long—consider breaking it into smaller, focused helper functions to improve readability and testability.
- The @Retry decorator on find_pipelinerun captures get_worker_config() at import time, so runtime changes to retry settings won’t apply—use lazy callables or a decorator factory to fetch config dynamically.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The handle_containerized_rm_request task is over 400 lines long—consider breaking it into smaller, focused helper functions to improve readability and testability.
- The @retry decorator on find_pipelinerun captures get_worker_config() at import time, so runtime changes to retry settings won’t apply—use lazy callables or a decorator factory to fetch config dynamically.
## Individual Comments
### Comment 1
<location> `iib/workers/tasks/oras_utils.py:213` </location>
<code_context>
+ refresh_indexdb_cache(_get_artifact_combined_tag(index_name, tag))
+
+
+def get_imagestream_artifact_pullspec(from_index: str) -> str:
+ """
+ Get the ImageStream pullspec for the index.db artifact.
</code_context>
<issue_to_address>
**suggestion (bug_risk):** get_imagestream_artifact_pullspec uses config keys without validation.
Add checks or default values for 'iib_index_db_artifact_template' and 'iib_index_db_imagestream_registry' to prevent KeyError if they are missing from the config.
</issue_to_address>
### Comment 2
<location> `iib/workers/config.py:45-51` </location>
<code_context>
}
iib_opm_pprof_lock_required_min_version = "1.29.0"
iib_image_push_template: str = '{registry}/iib-build:{request_id}'
+ iib_index_db_artifact_registry: str = None
+ iib_index_db_imagestream_registry: str = None
+ iib_index_db_artifact_tag_template: str = '{image_name}-{tag}'
+ iib_index_db_artifact_template: str = '{registry}/index-db:{tag}'
+ # Whether to use OpenShift ImageStream cache for index.db artifacts
+ # Requires OpenShift cluster with ImageStream configured
+ iib_use_imagestream_cache: bool = False
iib_index_image_output_registry: Optional[str] = None
iib_index_configs_gitlab_tokens_map: Optional[Dict[str, Dict[str, str]]] = None
</code_context>
<issue_to_address>
**suggestion (bug_risk):** New config options default to None, which may cause runtime errors.
Please set appropriate default values or add validation to prevent runtime errors when these config options are accessed.
Suggested implementation:
```python
# Default registry for index.db artifacts (set to empty string if not configured)
iib_index_db_artifact_registry: str = ''
# Default registry for index.db ImageStream (set to empty string if not configured)
iib_index_db_imagestream_registry: str = ''
iib_index_db_artifact_tag_template: str = '{image_name}-{tag}'
iib_index_db_artifact_template: str = '{registry}/index-db:{tag}'
# Whether to use OpenShift ImageStream cache for index.db artifacts
# Requires OpenShift cluster with ImageStream configured
iib_use_imagestream_cache: bool = False
```
If these config options are accessed elsewhere in the code, you should add validation logic such as:
```python
if not config.iib_index_db_artifact_registry:
raise ValueError("iib_index_db_artifact_registry must be set in the configuration.")
```
wherever they are used and a value is required. Adjust the default values if you have a known default registry or imagestream registry.
</issue_to_address>
### Comment 3
<location> `iib/workers/tasks/build_containerized_rm.py:178-182` </location>
<code_context>
+
+ # Remove operators from /configs
+ set_request_state(request_id, 'in_progress', 'Removing operators from catalog')
+ for operator in operators:
+ operator_path = os.path.join(localized_git_catalog_path, operator)
+ if os.path.exists(operator_path):
+ log.debug('Removing operator from catalog: %s', operator_path)
+ shutil.rmtree(operator_path)
</code_context>
<issue_to_address>
**suggestion:** Operator removal does not handle missing operator directories.
Consider logging a warning or raising an error when an operator directory is missing, if silent ignoring is not desired.
```suggestion
for operator in operators:
operator_path = os.path.join(localized_git_catalog_path, operator)
if os.path.exists(operator_path):
log.debug('Removing operator from catalog: %s', operator_path)
shutil.rmtree(operator_path)
else:
log.warning('Operator directory not found and cannot be removed: %s', operator_path)
```
</issue_to_address>
### Comment 4
<location> `iib/workers/tasks/containerized_utils.py:36-44` </location>
<code_context>
+ )
+
+ conf = get_worker_config()
+ if conf.get('iib_use_imagestream_cache', False):
+ # Verify index.db cache is synced. Refresh if not.
+ log.info('ImageStream cache is enabled. Checking cache sync status.')
+ cache_synced = verify_indexdb_cache_for_image(from_index)
+ if cache_synced:
+ log.info('Index.db cache is synced. Pulling from ImageStream.')
+ # Pull from ImageStream when digests match
+ imagestream_ref = get_imagestream_artifact_pullspec(from_index)
+ artifact_dir = get_oras_artifact(
+ imagestream_ref,
+ temp_dir,
</code_context>
<issue_to_address>
**suggestion (bug_risk):** ImageStream cache logic may not handle race conditions in cache refresh.
If refresh_indexdb_cache_for_image is asynchronous, a race condition may occur. Add a check to confirm the cache is updated before pulling from Quay.
</issue_to_address>
### Comment 5
<location> `iib/workers/tasks/containerized_utils.py:163-172` </location>
<code_context>
+ if original_index_db_digest:
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Restoring index.db artifact assumes oras copy will always succeed.
If oras copy fails due to registry issues or an invalid digest, the restoration may fail without notice. Please add error handling or alerts for unsuccessful restoration attempts.
Suggested implementation:
```python
if original_index_db_digest:
log.info("Restoring index.db artifact to original digest due to %s", reason)
try:
from iib.workers.tasks.oras_utils import get_indexdb_artifact_pullspec
from iib.workers.tasks.utils import run_cmd
# Get the v4.x artifact reference
v4x_artifact_ref = get_indexdb_artifact_pullspec(from_index)
# Extract registry and repository from the pullspec
# Format: quay.io/namespace/repo:tag -> we need quay.io/namespace/repo
# Attempt to restore index.db artifact using oras copy
try:
# Assuming oras_copy_cmd is constructed somewhere below
result = run_cmd(oras_copy_cmd)
except Exception as oras_error:
log.error(
"Failed to restore index.db artifact with oras copy for digest %s: %s",
original_index_db_digest,
oras_error,
)
# Optionally, you can raise the error or handle it as needed
except Exception as restore_error:
log.error("Exception during index.db restoration setup: %s", restore_error)
```
- Make sure `oras_copy_cmd` is defined and used in the restoration logic. If it's not present in the visible code, ensure the oras copy command is constructed and executed within the try/except block.
- Adjust error handling (e.g., raising exceptions or returning error codes) as appropriate for your application's needs.
</issue_to_address>
### Comment 6
<location> `tests/test_workers/test_tasks/test_konflux_utils.py:76-79` </location>
<code_context>
-
- # Verify
- assert result == []
+ # Test & Verify - After retries exhausted, IIBError gets wrapped in generic error
+ # The original "No pipelinerun found" error triggers retries,
+ # then final attempt gets caught by Exception handler
+ with pytest.raises(IIBError, match="Unexpected error while fetching pipelineruns for commit abc123: IIBError"):
+ find_pipelinerun("abc123")
</code_context>
<issue_to_address>
**suggestion (testing):** Test now expects IIBError after retries, but does not verify retry count or timing.
Add assertions or use mocks to confirm the retry logic triggers the expected number of attempts and adheres to the configured timing, ensuring the mechanism functions correctly beyond just raising the error.
Suggested implementation:
```python
mock_config.iib_retry_multiplier = 1 # Reduced to make test faster
mock_get_worker_config.return_value = mock_config
mock_client.list_namespaced_custom_object.return_value = {"items": []}
# Patch time.sleep to avoid actual delay and track calls
import time
from unittest import mock
retry_attempts = []
def fake_sleep(seconds):
retry_attempts.append(seconds)
with mock.patch("time.sleep", side_effect=fake_sleep):
# Test & Verify - After retries exhausted, IIBError gets wrapped in generic error
# The original "No pipelinerun found" error triggers retries,
# then final attempt gets caught by Exception handler
with pytest.raises(IIBError, match="Unexpected error while fetching pipelineruns for commit abc123: IIBError"):
find_pipelinerun("abc123")
# Assert retry logic: number of attempts and timing
# Assuming default retry count is 3 (adjust if different in your config)
assert len(retry_attempts) == mock_config.iib_retry_count
# Optionally, check the actual sleep values if exponential backoff is used
# assert retry_attempts == [mock_config.iib_retry_multiplier * (2 ** i) for i in range(mock_config.iib_retry_count)]
```
- If your retry count or multiplier is set elsewhere, adjust the assertion accordingly.
- If the retry logic uses a different backoff formula, update the sleep value assertion to match.
- Ensure `mock_config.iib_retry_count` is set to the expected number of retries for the test.
</issue_to_address>
### Comment 7
<location> `iib/workers/tasks/konflux_utils.py:259-261` </location>
<code_context>
pipeline_results = status.get('results', [])
if not pipeline_results:
pipeline_results = status.get('pipelineResults', [])
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use `or` for providing a fallback value ([`use-or-for-fallback`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/use-or-for-fallback))
```suggestion
pipeline_results = status.get('results', []) or status.get('pipelineResults', [])
```
<br/><details><summary>Explanation</summary>Thanks to the flexibility of Python's `or` operator, you can use a single
assignment statement, even if a variable can retrieve its value from various
sources. This is shorter and easier to read than using multiple assignments with
`if not` conditions.
</details>
</issue_to_address>
### Comment 8
<location> `iib/workers/tasks/build_containerized_rm.py:417` </location>
<code_context>
@app.task
@request_logger
@instrument_tracing(
span_name="workers.tasks.build.handle_containerized_rm_request",
attributes=get_binary_versions(),
)
def handle_containerized_rm_request(
operators: List[str],
request_id: int,
from_index: str,
binary_image: Optional[str] = None,
add_arches: Optional[Set[str]] = None,
overwrite_from_index: bool = False,
overwrite_from_index_token: Optional[str] = None,
distribution_scope: Optional[str] = None,
binary_image_config: Optional[Dict[str, Dict[str, str]]] = None,
build_tags: Optional[List[str]] = None,
index_to_gitlab_push_map: Optional[Dict[str, str]] = None,
) -> None:
"""
Coordinate the work needed to remove the input operators using containerized workflow.
This function uses Git-based workflows and Konflux pipelines instead of local builds.
:param list operators: a list of strings representing the name of the operators to
remove from the index image.
:param int request_id: the ID of the IIB build request
:param str from_index: the pull specification of the container image containing the index that
the index image build will be based from.
:param str binary_image: the pull specification of the container image where the opm binary
gets copied from.
:param set add_arches: the set of arches to build in addition to the arches ``from_index`` is
currently built for.
:param bool overwrite_from_index: if True, overwrite the input ``from_index`` with the built
index image.
:param str overwrite_from_index_token: the token used for overwriting the input
``from_index`` image. This is required to use ``overwrite_from_index``.
The format of the token must be in the format "user:password".
:param str distribution_scope: the scope for distribution of the index image, defaults to
``None``.
:param dict binary_image_config: the dict of config required to identify the appropriate
``binary_image`` to use.
:param list build_tags: List of tags which will be applied to intermediate index images.
:param dict index_to_gitlab_push_map: the dict mapping index images (keys) to GitLab repos
(values) in order to remove their catalogs from GitLab.
:raises IIBError: if the index image build fails.
"""
reset_docker_config()
set_request_state(request_id, 'in_progress', 'Preparing request for build')
# Prepare request
prebuild_info = prepare_request_for_build(
request_id,
RequestConfigAddRm(
_binary_image=binary_image,
from_index=from_index,
overwrite_from_index_token=overwrite_from_index_token,
add_arches=add_arches,
distribution_scope=distribution_scope,
binary_image_config=binary_image_config,
),
)
from_index_resolved = prebuild_info['from_index_resolved']
binary_image_resolved = prebuild_info['binary_image_resolved']
ocp_version = prebuild_info['ocp_version']
distribution_scope = prebuild_info['distribution_scope']
arches = prebuild_info['arches']
# Set OPM version
Opm.set_opm_version(from_index_resolved)
opm_version = Opm.opm_version
_update_index_image_build_state(request_id, prebuild_info)
mr_details: Optional[Dict[str, str]] = None
local_git_repo_path: Optional[str] = None
index_git_repo: Optional[str] = None
operators_in_db: Set[str] = set()
last_commit_sha: Optional[str] = None
output_pull_spec: Optional[str] = None
original_index_db_digest: Optional[str] = None
with tempfile.TemporaryDirectory(prefix=f'iib-{request_id}-') as temp_dir:
# Get Git repository information
# Strip tag and digest from from_index to get base image name for lookup
base_image_name = from_index.split('@')[0].rsplit(':', 1)[0]
if not index_to_gitlab_push_map or base_image_name not in index_to_gitlab_push_map:
raise IIBError(
f"Git repository mapping not found for from_index: {from_index} "
f"(base image: {base_image_name}). "
"index_to_gitlab_push_map is required."
)
index_git_repo = index_to_gitlab_push_map[base_image_name]
token_name, git_token = get_git_token(index_git_repo)
branch = ocp_version
# Clone Git repository
set_request_state(request_id, 'in_progress', 'Cloning Git repository')
local_git_repo_path = os.path.join(temp_dir, 'git', branch)
os.makedirs(local_git_repo_path, exist_ok=True)
clone_git_repo(index_git_repo, branch, token_name, git_token, local_git_repo_path)
localized_git_catalog_path = os.path.join(local_git_repo_path, 'configs')
if not os.path.exists(localized_git_catalog_path):
raise IIBError(f"Catalogs directory not found in {local_git_repo_path}")
# Remove operators from /configs
set_request_state(request_id, 'in_progress', 'Removing operators from catalog')
for operator in operators:
operator_path = os.path.join(localized_git_catalog_path, operator)
if os.path.exists(operator_path):
log.debug('Removing operator from catalog: %s', operator_path)
shutil.rmtree(operator_path)
# Remove operator deprecations
remove_operator_deprecations(
from_index_configs_dir=localized_git_catalog_path, operators=operators
)
# Pull index.db artifact (uses ImageStream cache if configured, otherwise pulls directly)
artifact_dir = pull_index_db_artifact(from_index, temp_dir)
# Find the index.db file in the artifact
index_db_path = os.path.join(artifact_dir, "index.db")
if not os.path.exists(index_db_path):
raise IIBError(f"Index.db file not found at {index_db_path}")
# Check if operators exist in index.db and remove if present
set_request_state(request_id, 'in_progress', 'Checking and removing from index database')
operators_in_db, index_db_path_verified = verify_operators_exists(
from_index=None,
base_dir=temp_dir,
operator_packages=operators,
overwrite_from_index_token=overwrite_from_index_token,
index_db_path=index_db_path,
)
# Use verified path or fall back to original
if index_db_path_verified:
index_db_path = index_db_path_verified
if operators_in_db:
log.info('Removing operators %s from index.db', operators_in_db)
# Remove from index.db and migrate to FBC
fbc_dir, _ = opm_registry_rm_fbc(
base_dir=temp_dir,
from_index=from_index_resolved,
operators=list[str](operators_in_db),
index_db_path=index_db_path,
)
# rename `catalog` directory because we need to use this name for
# final destination of catalog (defined in Dockerfile)
catalog_from_db = os.path.join(temp_dir, 'from_db')
os.rename(fbc_dir, catalog_from_db)
# Merge migrated FBC with existing FBC in Git repo
# overwrite data in `catalog_from_index` by data from `catalog_from_db`
# this adds changes on not opted in operators to final FBC
log.info('Merging migrated catalog with Git catalog')
merge_catalogs_dirs(catalog_from_db, localized_git_catalog_path)
fbc_dir_path = os.path.join(temp_dir, 'catalog')
# We need to regenerate file-based catalog because we merged changes
if os.path.exists(fbc_dir_path):
shutil.rmtree(fbc_dir_path)
# Copy catalog to correct location expected in Dockerfile
# Use copytree instead of move to preserve the configs directory in Git repo
shutil.copytree(localized_git_catalog_path, fbc_dir_path)
# Validate merged catalog
set_request_state(request_id, 'in_progress', 'Validating catalog')
opm_validate(fbc_dir_path)
# Write build metadata to a file to be added with the commit
set_request_state(request_id, 'in_progress', 'Writing build metadata')
write_build_metadata(
local_git_repo_path,
opm_version,
ocp_version,
distribution_scope,
binary_image_resolved,
request_id,
)
try:
# Commit changes and create PR or push directly
set_request_state(request_id, 'in_progress', 'Committing changes to Git repository')
log.info("Committing changes to Git repository. Triggering KONFLUX pipeline.")
# Determine if this is a throw-away request (no overwrite_from_index_token)
if not overwrite_from_index_token:
# Create MR for throw-away requests
mr_details = create_mr(
request_id=request_id,
local_repo_path=local_git_repo_path,
repo_url=index_git_repo,
branch=branch,
commit_message=f"IIB: Remove operators {', '.join(operators)} for request {request_id}",
)
log.info("Created merge request: %s", mr_details.get('mr_url'))
else:
# Push directly to branch
commit_and_push(
request_id=request_id,
local_repo_path=local_git_repo_path,
repo_url=index_git_repo,
branch=branch,
commit_message=f"IIB: Remove operators {', '.join(operators)} for request {request_id}",
)
# Get commit SHA before waiting for pipeline (while temp directory still exists)
last_commit_sha = get_last_commit_sha(local_repo_path=local_git_repo_path)
# Wait for Konflux pipeline
set_request_state(request_id, 'in_progress', 'Waiting on KONFLUX build')
# find_pipelinerun has retry decorator to handle delays in pipelinerun creation
pipelines = find_pipelinerun(last_commit_sha)
# Get the first pipelinerun (should typically be only one)
pipelinerun = pipelines[0]
pipelinerun_name = pipelinerun.get('metadata', {}).get('name')
if not pipelinerun_name:
raise IIBError("Pipelinerun name not found in pipeline metadata")
run = wait_for_pipeline_completion(pipelinerun_name)
# Extract IMAGE_URL from pipelinerun results
image_url = get_pipelinerun_image_url(pipelinerun_name, run)
# Build list of output pull specs to copy to
_tags = [str(request_id)]
if build_tags:
_tags.extend(build_tags)
conf = get_worker_config()
output_pull_specs = []
for tag in _tags:
output_pull_spec = conf['iib_image_push_template'].format(
registry=conf['iib_registry'], request_id=tag
)
output_pull_specs.append(output_pull_spec)
# Copy built index from Konflux to all output pull specs
set_request_state(request_id, 'in_progress', 'Copying built index to IIB registry')
for spec in output_pull_specs:
_skopeo_copy(
source=f'docker://{image_url}',
destination=f'docker://{spec}',
copy_all=True,
exc_msg=f'Failed to copy built index from Konflux to {spec}',
)
log.info("Successfully copied image to %s", spec)
# Use the first output_pull_spec as the primary one for request updates
output_pull_spec = output_pull_specs[0]
# Update request with final output
if not output_pull_spec:
raise IIBError(
"output_pull_spec was not set. "
"This should not happen if the pipeline completed successfully."
)
# Send an empty index_repo_map because the Git repository is already updated with the changes
_update_index_image_pull_spec(
output_pull_spec=output_pull_spec,
request_id=request_id,
arches=arches,
from_index=from_index,
overwrite_from_index=overwrite_from_index,
overwrite_from_index_token=overwrite_from_index_token,
resolved_prebuild_from_index=from_index_resolved,
add_or_rm=True,
is_image_fbc=True,
# Passing an empty index_repo_map is intentional. In IIB 1.0, if overwrite_from_index
# token is given, we push to git by default at the end of a request. In IIB 2.Oh!,
# the commit is pushed earlier to trigger a Konflux pipelinerun. So the old
# workflow isn't needed.
index_repo_map={},
rm_operators=operators,
)
# Push updated index.db if overwrite_from_index_token is provided
# We can push it directly from temp_dir since we're still inside the context manager
# Do it as the last step to avoid rolling back the index.db file if the pipeline fails.
if operators_in_db and index_db_path and os.path.exists(index_db_path):
# Get directory and filename separately to push only the filename
# This ensures ORAS extracts the file as just "index.db" without directory structure
index_db_dir = os.path.dirname(index_db_path)
index_db_filename = os.path.basename(index_db_path)
log.info('Pushing from directory: %s, filename: %s', index_db_dir, index_db_filename)
# Push with request_id tag irrespective of the overwrite_from_index_token
set_request_state(request_id, 'in_progress', 'Pushing updated index database')
image_name, tag = _get_name_and_tag_from_pullspec(from_index)
request_artifact_ref = conf['iib_index_db_artifact_template'].format(
registry=conf['iib_index_db_artifact_registry'],
tag=f"{_get_artifact_combined_tag(image_name, tag)}-{request_id}",
)
artifact_refs = [request_artifact_ref]
if overwrite_from_index_token:
# Get the current digest of v4.x tag before overwriting it
# This allows us to restore it if anything fails after the push
v4x_artifact_ref = get_indexdb_artifact_pullspec(from_index)
log.info('Capturing original digest of %s for potential rollback', v4x_artifact_ref)
original_index_db_digest = get_image_digest(v4x_artifact_ref)
log.info('Original index.db digest: %s', original_index_db_digest)
artifact_refs.append(v4x_artifact_ref)
for artifact_ref in artifact_refs:
push_oras_artifact(
artifact_ref=artifact_ref,
local_path=index_db_filename,
cwd=index_db_dir,
annotations={
'request_id': str(request_id),
'operation': 'remove_operators',
'operators': ','.join(operators),
},
)
log.info('Pushed %s to registry', artifact_ref)
# Close MR if it was opened
if mr_details and index_git_repo:
try:
close_mr(mr_details, index_git_repo)
log.info("Closed merge request: %s", mr_details.get('mr_url'))
except IIBError as e:
log.warning("Failed to close merge request: %s", e)
set_request_state(
request_id,
'complete',
f"The operator(s) {', '.join(operators)} were successfully removed from the index image",
)
except Exception as e:
cleanup_on_failure(
mr_details=mr_details,
last_commit_sha=last_commit_sha,
index_git_repo=index_git_repo,
overwrite_from_index=overwrite_from_index,
request_id=request_id,
from_index=from_index,
index_repo_map=index_to_gitlab_push_map or {},
original_index_db_digest=original_index_db_digest,
reason=f"error: {e}",
)
raise IIBError(f"Failed to remove operators: {e}")
# Reset Docker config for the next request. This is a fail safe.
reset_docker_config()
</code_context>
<issue_to_address>
**suggestion (code-quality):** We've found these issues:
- Explicitly raise from a previous error ([`raise-from-previous-error`](https://docs.sourcery.ai/Reference/Default-Rules/suggestions/raise-from-previous-error/))
- Low code quality found in handle\_containerized\_rm\_request - 11% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))
```suggestion
raise IIBError(f"Failed to remove operators: {e}") from e
```
<br/><details><summary>Explanation</summary>
The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.
How can you solve this?
It might be worth refactoring this function to make it shorter and more readable.
- Reduce the function length by extracting pieces of functionality out into
their own functions. This is the most important thing you can do - ideally a
function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
sits together within the function rather than being scattered.</details>
</issue_to_address>
### Comment 9
<location> `iib/workers/tasks/containerized_utils.py:39-40` </location>
<code_context>
def pull_index_db_artifact(from_index: str, temp_dir: str) -> str:
"""
Pull index.db artifact from registry, using ImageStream cache if available.
This function determines whether to use OpenShift ImageStream cache or pull directly
from the registry based on the iib_use_imagestream_cache configuration.
:param str from_index: The from_index pullspec
:param str temp_dir: Temporary directory where the artifact will be extracted
:return: Path to the directory containing the extracted artifact
:rtype: str
:raises IIBError: If the pull operation fails
"""
from iib.workers.tasks.oras_utils import (
get_indexdb_artifact_pullspec,
get_imagestream_artifact_pullspec,
get_oras_artifact,
refresh_indexdb_cache_for_image,
verify_indexdb_cache_for_image,
)
conf = get_worker_config()
if conf.get('iib_use_imagestream_cache', False):
# Verify index.db cache is synced. Refresh if not.
log.info('ImageStream cache is enabled. Checking cache sync status.')
cache_synced = verify_indexdb_cache_for_image(from_index)
if cache_synced:
log.info('Index.db cache is synced. Pulling from ImageStream.')
# Pull from ImageStream when digests match
imagestream_ref = get_imagestream_artifact_pullspec(from_index)
artifact_dir = get_oras_artifact(
imagestream_ref,
temp_dir,
)
else:
log.info('Index.db cache is not synced. Refreshing and pulling from Quay.')
refresh_indexdb_cache_for_image(from_index)
# Pull directly from Quay after triggering refresh
artifact_ref = get_indexdb_artifact_pullspec(from_index)
artifact_dir = get_oras_artifact(
artifact_ref,
temp_dir,
)
else:
# Pull directly from Quay without ImageStream cache
log.info('ImageStream cache is disabled. Pulling index.db artifact directly from registry.')
artifact_ref = get_indexdb_artifact_pullspec(from_index)
artifact_dir = get_oras_artifact(
artifact_ref,
temp_dir,
)
return artifact_dir
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if cache_synced := verify_indexdb_cache_for_image(from_index):
```
</issue_to_address>
### Comment 10
<location> `iib/workers/tasks/containerized_utils.py:166` </location>
<code_context>
def cleanup_on_failure(
mr_details: Optional[Dict[str, str]],
last_commit_sha: Optional[str],
index_git_repo: Optional[str],
overwrite_from_index: bool,
request_id: int,
from_index: str,
index_repo_map: Dict[str, str],
original_index_db_digest: Optional[str] = None,
reason: str = "error",
) -> None:
"""
Clean up Git changes and index.db artifacts on failure.
If a merge request was created, it will be closed (since the commit is only in a
feature branch). If changes were pushed directly to the main branch, the commit
will be reverted. If the index.db artifact was pushed to the v4.x tag, it will be
restored to the original digest.
:param Optional[Dict[str, str]] mr_details: Details of the merge request if one was created
:param Optional[str] last_commit_sha: The SHA of the last commit
:param Optional[str] index_git_repo: URL of the Git repository
:param bool overwrite_from_index: Whether to overwrite the from_index
:param int request_id: The IIB request ID
:param str from_index: The from_index pullspec
:param Dict[str, str] index_repo_map: Mapping of index images to Git repositories
:param Optional[str] original_index_db_digest: Original digest of index.db before overwrite
:param str reason: Reason for the cleanup (used in log messages)
"""
if mr_details and index_git_repo:
# If we created an MR, just close it (commit is only in feature branch)
log.info("Closing merge request due to %s", reason)
try:
from iib.workers.tasks.git_utils import close_mr
close_mr(mr_details, index_git_repo)
log.info("Closed merge request: %s", mr_details.get('mr_url'))
except Exception as close_error:
log.warning("Failed to close merge request: %s", close_error)
elif overwrite_from_index and last_commit_sha:
# If we pushed directly, revert the commit
log.error("Reverting commit due to %s", reason)
try:
from iib.workers.tasks.git_utils import revert_last_commit
revert_last_commit(
request_id=request_id,
from_index=from_index,
index_repo_map=index_repo_map,
)
except Exception as revert_error:
log.error("Failed to revert commit: %s", revert_error)
else:
log.error("Neither MR nor commit to revert. No cleanup needed for %s", reason)
# Restore index.db artifact to original digest if it was overwritten
if original_index_db_digest:
log.info("Restoring index.db artifact to original digest due to %s", reason)
try:
from iib.workers.tasks.oras_utils import get_indexdb_artifact_pullspec
from iib.workers.tasks.utils import run_cmd
# Get the v4.x artifact reference
v4x_artifact_ref = get_indexdb_artifact_pullspec(from_index)
# Extract registry and repository from the pullspec
# Format: quay.io/namespace/repo:tag -> we need quay.io/namespace/repo
artifact_name = v4x_artifact_ref.rsplit(':', 1)[0]
# Use oras copy to restore the old digest to v4.x tag
# This is a registry-to-registry copy, no download needed
source_ref = f'{artifact_name}@{original_index_db_digest}'
log.info("Restoring %s from %s", v4x_artifact_ref, source_ref)
run_cmd(
['oras', 'copy', source_ref, v4x_artifact_ref],
exc_msg=f'Failed to restore index.db artifact from {source_ref} to {v4x_artifact_ref}',
)
log.info("Successfully restored index.db artifact to original digest")
except Exception as restore_error:
log.error("Failed to restore index.db artifact: %s", restore_error)
</code_context>
<issue_to_address>
**issue (code-quality):** Extract code out into function ([`extract-method`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/extract-method/))
</issue_to_address>
### Comment 11
<location> `iib/workers/tasks/konflux_utils.py:134` </location>
<code_context>
@retry(
before_sleep=before_sleep_log(log, logging.WARNING),
reraise=True,
retry=retry_if_exception_type(IIBError),
stop=stop_after_attempt(get_worker_config().iib_total_attempts),
wait=wait_chain(wait_exponential(multiplier=get_worker_config().iib_retry_multiplier)),
)
def find_pipelinerun(commit_sha: str) -> List[Dict[str, Any]]:
"""
Find the Konflux pipelinerun triggered by the git commit.
This function will retry if no pipelineruns are found (empty list), as it may take
a few seconds for the pipelinerun to start after a commit is pushed.
:param str commit_sha: The git commit SHA to search for
:return: List of pipelinerun objects matching the commit SHA
:rtype: List[Dict[str, Any]]
:raises IIBError: If there's an error fetching pipelineruns or no pipelineruns found after retries
"""
try:
log.info("Searching for pipelineruns with commit SHA: %s", commit_sha)
v1_client = _get_kubernetes_client()
worker_config = get_worker_config()
namespace = worker_config.iib_konflux_namespace
runs = v1_client.list_namespaced_custom_object(
group="tekton.dev",
version="v1",
namespace=namespace,
plural="pipelineruns",
label_selector=f"pipelinesascode.tekton.dev/sha={commit_sha}",
)
items = runs.get("items", [])
log.info("Found %s pipelinerun(s) for commit %s", len(items), commit_sha)
# Raise IIBError if no pipelineruns found to trigger retry
if not items:
raise IIBError(f"No pipelinerun found for commit SHA: {commit_sha}")
return items
except ApiException as e:
error_msg = f"Failed to fetch pipelineruns for commit {commit_sha}: API error {e.status}"
log.error("Kubernetes API error while fetching pipelineruns: %s - %s", e.status, e.reason)
raise IIBError(error_msg)
except Exception as e:
error_msg = (
f"Unexpected error while fetching pipelineruns for commit {commit_sha}: "
f"{type(e).__name__}"
)
log.error("Unexpected error while fetching pipelineruns: %s", type(e).__name__)
raise IIBError(error_msg)
</code_context>
<issue_to_address>
**issue (code-quality):** Explicitly raise from a previous error [×2] ([`raise-from-previous-error`](https://docs.sourcery.ai/Reference/Default-Rules/suggestions/raise-from-previous-error/))
</issue_to_address>
### Comment 12
<location> `iib/workers/tasks/konflux_utils.py:267-268` </location>
<code_context>
def get_pipelinerun_image_url(pipelinerun_name: str, run: Dict[str, Any]) -> str:
"""
Extract IMAGE_URL from a completed pipelinerun's results.
:param str pipelinerun_name: Name of the pipelinerun
:param Dict[str, Any] run: The pipelinerun object
:return: The IMAGE_URL value from the pipelinerun results
:rtype: str
:raises IIBError: If IMAGE_URL is not found in the pipelinerun results
"""
status = run.get('status', {})
# Check for 'results' (Konflux format) first, then fall back to 'pipelineResults' (older Tekton)
pipeline_results = status.get('results', [])
if not pipeline_results:
pipeline_results = status.get('pipelineResults', [])
log.info("Found %d pipeline results for %s", len(pipeline_results), pipelinerun_name)
for result in pipeline_results:
if result.get('name') == 'IMAGE_URL':
image_url = result.get('value')
if image_url:
# Strip whitespace (including newlines) from the URL
image_url = image_url.strip()
log.info("Extracted IMAGE_URL from pipelinerun %s: %s", pipelinerun_name, image_url)
return image_url
# If not found, log for debugging
log.error("IMAGE_URL not found in pipelinerun %s. Available results: %s",
pipelinerun_name, [r.get('name') for r in pipeline_results])
raise IIBError(f"IMAGE_URL not found in pipelinerun {pipelinerun_name} results")
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if image_url := result.get('value'):
```
</issue_to_address>
### Comment 13
<location> `iib/workers/tasks/oras_utils.py:116` </location>
<code_context>
@instrument_tracing(span_name="workers.tasks.oras_utils.push_oras_artifact")
def push_oras_artifact(
artifact_ref: str,
local_path: str,
artifact_type: str = "application/vnd.sqlite",
registry_auths: Optional[Dict[str, Any]] = None,
annotations: Optional[Dict[str, str]] = None,
cwd: Optional[str] = None,
) -> None:
"""
Push a local artifact to an OCI registry using ORAS.
This function is equivalent to: `oras push {artifact_ref} {local_path}:{artifact_type}`
:param str artifact_ref: OCI artifact reference to push to (e.g., 'quay.io/repo/repo:tag')
:param str local_path: Local path to the artifact file. Should be a relative path.
When using cwd, this should be a relative path (typically just
the filename) relative to the cwd directory.
:param str artifact_type: MIME type of the artifact (default: 'application/vnd.sqlite')
:param dict registry_auths: Optional dockerconfig.json auth information for private registries
:param dict annotations: Optional annotations to add to the artifact
:param str cwd: Optional working directory for the ORAS command. When provided, local_path
should be relative to this directory (e.g., just the filename).
:raises IIBError: If the push operation fails
"""
log.info('Pushing artifact from %s to %s with type %s', local_path, artifact_ref, artifact_type)
if cwd:
log.info('Using working directory: %s', cwd)
# Construct the full path for validation
full_path = os.path.join(cwd, local_path) if cwd else local_path
if not os.path.exists(full_path):
raise IIBError(f'Local artifact path does not exist: {full_path}')
# Build ORAS push command
cmd = ['oras', 'push', artifact_ref, f'{local_path}:{artifact_type}']
# Add --disable-path-validation flag for absolute paths
if os.path.isabs(local_path):
cmd.append('--disable-path-validation')
# Add annotations if provided
if annotations:
for key, value in annotations.items():
cmd.extend(['--annotation', f'{key}={value}'])
# Prepare parameters for run_cmd
params = {}
if cwd:
params['cwd'] = cwd
# Use namespace-specific registry authentication if provided
with set_registry_auths(registry_auths, use_empty_config=True):
try:
run_cmd(cmd, params=params, exc_msg=f'Failed to push OCI artifact to {artifact_ref}')
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}')
</code_context>
<issue_to_address>
**suggestion (code-quality):** Explicitly raise from a previous error ([`raise-from-previous-error`](https://docs.sourcery.ai/Reference/Default-Rules/suggestions/raise-from-previous-error/))
```suggestion
raise IIBError(f'Failed to push OCI artifact to {artifact_ref}: {e}') from e
```
</issue_to_address>
### Comment 14
<location> `iib/workers/tasks/oras_utils.py:229-231` </location>
<code_context>
def get_imagestream_artifact_pullspec(from_index: str) -> str:
"""
Get the ImageStream pullspec for the index.db artifact.
This function constructs the internal OpenShift ImageStream pullspec that can be used
to pull the index.db artifact from the cached ImageStream instead of directly from Quay.
:param str from_index: The from_index pullspec
:return: ImageStream pullspec for the artifact
:rtype: str
"""
conf = get_worker_config()
image_name, tag = _get_name_and_tag_from_pullspec(from_index)
combined_tag = _get_artifact_combined_tag(image_name, tag)
# ImageStream pullspec format: image-registry.openshift-image-registry.svc:5000/{namespace}/index-db:{combined_tag}
imagestream_pullspec = conf['iib_index_db_artifact_template'].format(
registry=conf['iib_index_db_imagestream_registry'], tag=combined_tag
)
return imagestream_pullspec
</code_context>
<issue_to_address>
**issue (code-quality):** Inline variable that is immediately returned ([`inline-immediately-returned-variable`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/inline-immediately-returned-variable/))
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| refresh_indexdb_cache(_get_artifact_combined_tag(index_name, tag)) | ||
|
|
||
|
|
||
| def get_imagestream_artifact_pullspec(from_index: str) -> str: |
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 (bug_risk): get_imagestream_artifact_pullspec uses config keys without validation.
Add checks or default values for 'iib_index_db_artifact_template' and 'iib_index_db_imagestream_registry' to prevent KeyError if they are missing from the config.
| iib_index_db_artifact_registry: str = None | ||
| iib_index_db_imagestream_registry: str = None | ||
| iib_index_db_artifact_tag_template: str = '{image_name}-{tag}' | ||
| iib_index_db_artifact_template: str = '{registry}/index-db:{tag}' | ||
| # Whether to use OpenShift ImageStream cache for index.db artifacts | ||
| # Requires OpenShift cluster with ImageStream configured | ||
| iib_use_imagestream_cache: bool = False |
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 (bug_risk): New config options default to None, which may cause runtime errors.
Please set appropriate default values or add validation to prevent runtime errors when these config options are accessed.
Suggested implementation:
# Default registry for index.db artifacts (set to empty string if not configured)
iib_index_db_artifact_registry: str = ''
# Default registry for index.db ImageStream (set to empty string if not configured)
iib_index_db_imagestream_registry: str = ''
iib_index_db_artifact_tag_template: str = '{image_name}-{tag}'
iib_index_db_artifact_template: str = '{registry}/index-db:{tag}'
# Whether to use OpenShift ImageStream cache for index.db artifacts
# Requires OpenShift cluster with ImageStream configured
iib_use_imagestream_cache: bool = FalseIf these config options are accessed elsewhere in the code, you should add validation logic such as:
if not config.iib_index_db_artifact_registry:
raise ValueError("iib_index_db_artifact_registry must be set in the configuration.")wherever they are used and a value is required. Adjust the default values if you have a known default registry or imagestream registry.
| for operator in operators: | ||
| operator_path = os.path.join(localized_git_catalog_path, operator) | ||
| if os.path.exists(operator_path): | ||
| log.debug('Removing operator from catalog: %s', operator_path) | ||
| shutil.rmtree(operator_path) |
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: Operator removal does not handle missing operator directories.
Consider logging a warning or raising an error when an operator directory is missing, if silent ignoring is not desired.
| for operator in operators: | |
| operator_path = os.path.join(localized_git_catalog_path, operator) | |
| if os.path.exists(operator_path): | |
| log.debug('Removing operator from catalog: %s', operator_path) | |
| shutil.rmtree(operator_path) | |
| for operator in operators: | |
| operator_path = os.path.join(localized_git_catalog_path, operator) | |
| if os.path.exists(operator_path): | |
| log.debug('Removing operator from catalog: %s', operator_path) | |
| shutil.rmtree(operator_path) | |
| else: | |
| log.warning('Operator directory not found and cannot be removed: %s', operator_path) |
| if conf.get('iib_use_imagestream_cache', False): | ||
| # Verify index.db cache is synced. Refresh if not. | ||
| log.info('ImageStream cache is enabled. Checking cache sync status.') | ||
| cache_synced = verify_indexdb_cache_for_image(from_index) | ||
| if cache_synced: | ||
| log.info('Index.db cache is synced. Pulling from ImageStream.') | ||
| # Pull from ImageStream when digests match | ||
| imagestream_ref = get_imagestream_artifact_pullspec(from_index) | ||
| artifact_dir = get_oras_artifact( |
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 (bug_risk): ImageStream cache logic may not handle race conditions in cache refresh.
If refresh_indexdb_cache_for_image is asynchronous, a race condition may occur. Add a check to confirm the cache is updated before pulling from Quay.
| if original_index_db_digest: | ||
| log.info("Restoring index.db artifact to original digest due to %s", reason) | ||
| try: | ||
| from iib.workers.tasks.oras_utils import get_indexdb_artifact_pullspec | ||
| from iib.workers.tasks.utils import run_cmd | ||
|
|
||
| # Get the v4.x artifact reference | ||
| v4x_artifact_ref = get_indexdb_artifact_pullspec(from_index) | ||
|
|
||
| # Extract registry and repository from the pullspec |
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 (bug_risk): Restoring index.db artifact assumes oras copy will always succeed.
If oras copy fails due to registry issues or an invalid digest, the restoration may fail without notice. Please add error handling or alerts for unsuccessful restoration attempts.
Suggested implementation:
if original_index_db_digest:
log.info("Restoring index.db artifact to original digest due to %s", reason)
try:
from iib.workers.tasks.oras_utils import get_indexdb_artifact_pullspec
from iib.workers.tasks.utils import run_cmd
# Get the v4.x artifact reference
v4x_artifact_ref = get_indexdb_artifact_pullspec(from_index)
# Extract registry and repository from the pullspec
# Format: quay.io/namespace/repo:tag -> we need quay.io/namespace/repo
# Attempt to restore index.db artifact using oras copy
try:
# Assuming oras_copy_cmd is constructed somewhere below
result = run_cmd(oras_copy_cmd)
except Exception as oras_error:
log.error(
"Failed to restore index.db artifact with oras copy for digest %s: %s",
original_index_db_digest,
oras_error,
)
# Optionally, you can raise the error or handle it as needed
except Exception as restore_error:
log.error("Exception during index.db restoration setup: %s", restore_error)- Make sure
oras_copy_cmdis defined and used in the restoration logic. If it's not present in the visible code, ensure the oras copy command is constructed and executed within the try/except block. - Adjust error handling (e.g., raising exceptions or returning error codes) as appropriate for your application's needs.
| if original_index_db_digest: | ||
| log.info("Restoring index.db artifact to original digest due to %s", reason) | ||
| try: | ||
| from iib.workers.tasks.oras_utils import get_indexdb_artifact_pullspec |
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.
issue (code-quality): Extract code out into function (extract-method)
| :raises IIBError: If there's an error fetching pipelineruns | ||
| :raises IIBError: If there's an error fetching pipelineruns or no pipelineruns found after retries | ||
| """ | ||
| try: |
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.
issue (code-quality): Explicitly raise from a previous error [×2] (raise-from-previous-error)
| image_url = result.get('value') | ||
| if image_url: |
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 (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)
| image_url = result.get('value') | |
| if image_url: | |
| if image_url := result.get('value'): |
| run_cmd(cmd, params=params, exc_msg=f'Failed to push OCI artifact to {artifact_ref}') | ||
| 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}') |
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 (code-quality): Explicitly raise from a previous error (raise-from-previous-error)
| raise IIBError(f'Failed to push OCI artifact to {artifact_ref}: {e}') | |
| raise IIBError(f'Failed to push OCI artifact to {artifact_ref}: {e}') from e |
| imagestream_pullspec = conf['iib_index_db_artifact_template'].format( | ||
| registry=conf['iib_index_db_imagestream_registry'], tag=combined_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.
issue (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)
Refers to CLOUDDST-28865
Assisted-by: Cursor
Summary by Sourcery
Implement a new containerized workflow handler for removal requests, add supporting utilities for index.db artifact caching and Git/Celery integration, and enhance Konflux and ORAS task modules with retry logic, image URL extraction, and execution context improvements while updating configuration and tests.
New Features:
Enhancements:
Tests: