-
Notifications
You must be signed in to change notification settings - Fork 28
Fix index.db namespace collisions and support divergent-tag builds #1378
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?
Changes from all commits
a4ed53b
fffa76e
42ce2d9
b0db932
de380a4
31d072a
abcb41a
d60faf9
66d231b
8861631
0da753d
4f4df4c
961cd7e
ee6aa59
b73a69f
b3210b7
5124112
bde9a0c
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 |
|---|---|---|
|
|
@@ -57,3 +57,4 @@ Key modules: | |
| - **Never edit existing Alembic migrations** — generate a new revision instead. | ||
| - **API ↔ Worker task signatures must match** — renaming args silently breaks in-flight requests. | ||
| - **Avoid making changes to the old worker architecture** - Always ask before making changes to the following functions - `handle_add_request`, `handle_rm_request`, `handle_merge_request`, `handle_create_empty_index_request`, `handle_fbc_operation_request`, `handle_regenerate_bundle_request` | ||
| - **Divergent-tag requests never merge their MR** — they reuse the base OCP branch's Konflux Component and must stay throw-away; the guard is `overwrite_from_index and not sources.is_divergent`. Never source their index.db from ORAS. | ||
|
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. [high] protected-path AGENTS.md is a protected governance file. This PR modifies it to document the divergent-tag invariant but has no linked issue authorizing changes to protected paths. Human approval is always required for protected-path changes regardless of context. Suggested fix: Link an issue authorizing the AGENTS.md change, or obtain explicit human approval for the protected-path modification. 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. [high] protected-path PR modifies a protected governance file (AGENTS.md) without a linked issue providing authorization. Human approval is required for all protected-path changes regardless of context. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -253,6 +253,39 @@ The worker configuration is in `docker/containerized/worker_config.py`. This fil | |
| - Includes the containerized task modules | ||
| - Validates required configuration on startup | ||
|
|
||
| ## Git Branch and Tag Semantics | ||
|
|
||
| The Git branch used for a request's catalog is keyed on the **image tag**, not a fixed OCP-version mapping. For the existing fleet this is a no-op, since prod index tags already equal the OCP version (e.g. `v4.19` → branch `v4.19`). | ||
|
|
||
| ### Onboarding a Non-OCP Tag | ||
|
|
||
| To onboard a tag that isn't an OCP version (e.g. a custom or pre-release tag), a maintainer must: | ||
|
|
||
| 1. Create a Git branch in the catalog repository named exactly after the tag. | ||
| 2. Provision a Konflux Component for that branch, so pushes to it trigger a PipelineRun. | ||
|
|
||
| Once both exist, requests against that tag build and push normally, and `overwrite_from_index` is honored like any other branch. | ||
|
|
||
| ### Divergent Tags (No Matching Branch) | ||
|
|
||
| If a request targets a tag with no corresponding Git branch — for example a timestamped, point-in-time tag cut from an existing index — IIB treats it as a **divergent tag**: | ||
|
|
||
| - Configs and `index.db` are extracted directly from the index image (unprivileged), not sourced from ORAS. Only the FBC configs and the hidden `index.db` are extracted; if the image carries no hidden `index.db`, the request fails ("no index.db found, onboard the image to build") — there is no labeled-db or empty-db fallback. | ||
| - The build reuses the base OCP branch's existing Konflux Component via a throw-away merge request. | ||
| - That MR is **never merged** — it is always closed after the pipeline completes (or on failure), regardless of outcome. | ||
| - `overwrite_from_index` is **rejected** for divergent-tag requests; there is no direct-push path. | ||
|
|
||
| This lets IIB build and validate a one-off tag without requiring per-tag branch/Component provisioning. | ||
|
|
||
| ## Index DB Artifact and ImageStream Tag Naming | ||
|
|
||
| Cached `index.db` artifact tags (ORAS) and ImageStream tags are keyed on the index image's content (manifest) digest — `idb-<sha256>` — resolved via `skopeo inspect`, not on its pullspec. Because the key is the content itself, it is both namespace-safe and promotion-safe: | ||
|
|
||
| - **Namespace-safe:** two images that share a repository name in different registry namespaces (e.g. `quay.io/redhat/my-index:v4.17` vs `quay.io/redhat-pending/my-index:v4.17`) have different content and therefore different digests, so they never collide on the same cache tag. | ||
| - **Promotion-safe:** the same image content addressed by different pullspecs after a release or mirror (e.g. `quay.io/my-namespace/iib-pub:v4.17` → `registry.access.redhat.com/some-namespace/operator-index:v4.17`) preserves its manifest digest, so both pullspecs resolve to the *same* cache entry and share one `index.db`. | ||
|
|
||
| Cache entries written under the previous pullspec-derived naming scheme are orphaned by this change — they are not migrated in place. On the normal path IIB never falls back to extracting `index.db` from the image: if the digest-keyed artifact is missing, the request fails with a "no index.db found for the image, onboard the image to build" error, and the image must be onboarded (which populates the artifact) before it can be built. Orphaned entries are cleaned up by the existing cache-pruning process rather than any code path in this workflow. | ||
|
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. [high] scope-creep The content-key migration orphans every existing ORAS-cached index.db artifact in production. The README explicitly documents this: cached entries under the previous naming scheme are orphaned, and affected images fail with 'no index.db found' until re-onboarded. Every onboarded image will begin failing immediately after deployment with no fallback path or migration mechanism. Suggested fix: Add an explicit deployment checklist item, or introduce a transient fallback that checks the digest-keyed artifact and, if absent, falls back to the old pullspec-keyed artifact for one release cycle.
Collaborator
Author
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. this is intentional. There are no production index.db atm |
||
|
|
||
| ## Differences from Traditional Workflow | ||
|
|
||
| | Aspect | Traditional Workflow | Containerized Workflow | | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,15 @@ class IIBError(BaseException): | |
| """An error was encountered in IIB.""" | ||
|
|
||
|
|
||
| class FileNotFoundInImageError(IIBError): | ||
|
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. [low] docstring-convention All existing exception classes use single-sentence docstrings. FileNotFoundInImageError introduces a multi-paragraph rationale docstring, deviating from the established convention. Suggested fix: Collapse to a single sentence; move rationale to an inline comment. |
||
| """A requested path was not present in a container image. | ||
|
|
||
| Subclasses IIBError so existing ``except IIBError`` handlers still catch it, | ||
| while letting callers distinguish a genuinely absent path from a real | ||
| extraction failure (registry, OCI parsing, layer, or tar error). | ||
| """ | ||
|
|
||
|
|
||
| class ValidationError(BaseException): | ||
| """Denote invalid input.""" | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,7 +71,7 @@ class Config(object): | |
| iib_index_db_oras_auth_path: str = os.path.join( | ||
| os.path.expanduser('~'), '.docker', 'oras', 'config.json' | ||
| ) | ||
| iib_index_db_artifact_tag_template: str = '{image_name}-{tag}' | ||
| iib_index_db_artifact_tag_template: str = 'idb-{digest}' | ||
|
lipoja marked this conversation as resolved.
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. [low] config-migration The iib_index_db_artifact_tag_template default changed from '{image_name}-{tag}' to 'idb-{digest}'. Deployments customizing this config with old placeholders will get an immediate KeyError at runtime when .format(digest=...) is called -- a loud crash, not silent corruption. Suggested fix: Add a startup validation check that the template contains '{digest}', or document the config migration in release notes. 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. [medium] breaking-config-interface The format variable names in iib_index_db_artifact_tag_template changed from {image_name} and {tag} to {digest}. Any deployment overriding this setting in a site-level settings.py will get an unhandled KeyError at runtime when _get_artifact_combined_tag calls .format(digest=...) on a template referencing {image_name} and {tag}. Suggested fix: Validate the config key's format string at startup (check that {digest} is present, warn if {image_name}/{tag} are found), or document that the template format is not operator-configurable. |
||
| 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,7 +34,6 @@ | |
| opm_validate, | ||
| ) | ||
| from iib.workers.tasks.oras_utils import ( | ||
| _get_artifact_combined_tag, | ||
| _get_name_and_tag_from_pullspec, | ||
| get_oras_artifact, | ||
| ) | ||
|
|
@@ -176,7 +175,6 @@ def handle_containerized_create_empty_index_request( | |
| index_git_repo: Optional[str] = None | ||
| 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: | ||
| branch = ocp_version | ||
|
|
@@ -199,11 +197,16 @@ def handle_containerized_create_empty_index_request( | |
| conf = get_worker_config() | ||
| empty_tag = conf.get('iib_empty_index_db_tag', 'empty') | ||
|
|
||
| # Construct the pullspec for the empty index.db artifact | ||
| # Construct the pullspec for the empty index.db artifact. | ||
| # This tag intentionally does NOT use the content-digest key that | ||
| # _get_artifact_combined_tag derives for real indexes: the empty index.db | ||
| # is a shared, content-free seed artifact keyed only by image name + | ||
| # "empty", so it is deliberately reusable across indexes rather than tied | ||
| # to any single image's manifest digest. | ||
| image_name, _ = _get_name_and_tag_from_pullspec(from_index) | ||
|
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. [medium] API-shape-inconsistency The create_empty_index handler bypasses _get_artifact_combined_tag by inlining a hardcoded format string. This is intentionally different (the empty artifact is a shared, namespace-agnostic sentinel), but the code lacks an explanatory comment. A future maintainer could fix this and break the shared semantics. Suggested fix: Add an inline comment explaining that the empty-tag artifact is intentionally shared across namespaces and should not include the pullspec hash. 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. [low] data-integrity Empty index.db artifact tag intentionally omits pullspec hash (shared namespace-agnostic seed). Documented design decision. |
||
| empty_artifact_ref = conf['iib_index_db_artifact_template'].format( | ||
| registry=conf['iib_index_db_artifact_registry'], | ||
| tag=_get_artifact_combined_tag(image_name, empty_tag), | ||
| tag=f"{image_name}-{empty_tag}", | ||
| ) | ||
|
|
||
| log.info('Fetching empty index.db from %s', empty_artifact_ref) | ||
|
|
@@ -344,12 +347,13 @@ def handle_containerized_create_empty_index_request( | |
|
|
||
| # Push the empty index.db with request ID tag | ||
| # Since overwrite_from_index is False, this will only push with request_id tag | ||
| # and will not overwrite the v4.x tag | ||
| original_index_db_digest = push_index_db_artifact( | ||
| # and will not overwrite the current artifact | ||
| push_index_db_artifact( | ||
| request_id=request_id, | ||
| from_index=from_index, | ||
| index_db_path=str(index_db_path), | ||
| operators=[], # Empty list since we're creating an empty index | ||
| output_image=image_url, | ||
| overwrite_from_index=False, # Always False for create_empty_index | ||
| request_type='create_empty_index', | ||
| ) | ||
|
|
@@ -372,7 +376,6 @@ def handle_containerized_create_empty_index_request( | |
| 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 create empty index: {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.
[low] scope-creep
The PR adds a project-level invariant rule to AGENTS.md that encodes the design decisions of the feature being introduced in this same PR. While documenting a feature alongside its implementation is reasonable, the rule should be reviewed independently as it establishes architectural precedent.