Skip to content

ART-21851: Include component name in base-image snapshot name - #2979

Merged
ashwindasr merged 1 commit into
openshift-eng:mainfrom
joepvd:fix-snapshot-name-collision
Aug 4, 2026
Merged

ART-21851: Include component name in base-image snapshot name#2979
ashwindasr merged 1 commit into
openshift-eng:mainfrom
joepvd:fix-snapshot-name-collision

Conversation

@joepvd

@joepvd joepvd commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • When multiple base images were snapshotted within the same second, they got identical snapshot names ({group}-base-image-{timestamp}), causing 409 Conflict errors from the Kubernetes API
  • Include the Konflux component name in the snapshot name ({group}-{component}-{timestamp}) so each image gets a unique name even at second-level timestamp granularity
  • Component names are already k8s DNS label safe; only truncation is needed to stay within the 63-char limit

Example failure:

2026-05-29 10:43:57,111 doozerlib.backend.konflux_client INFO Creating appstudio.redhat.com/v1alpha1/Snapshot ocp-art-tenant/openshift-4-23-base-image-20260529104357...
2026-05-29 10:43:57,183 doozerlib.backend.konflux_client INFO Created appstudio.redhat.com/v1alpha1/Snapshot ocp-art-tenant/openshift-4-23-base-image-20260529104357
2026-05-29 10:43:57,183 artcommonlib INFO [containers/openshift-base-rhel9] Created snapshot openshift-4-23-base-image-20260529104357
2026-05-29 10:43:57,183 doozerlib.backend.konflux_client INFO Creating appstudio.redhat.com/v1alpha1/Snapshot ocp-art-tenant/openshift-4-23-base-image-20260529104357...
2026-05-29 10:43:57,237 artcommonlib ERROR [containers/openshift-base-nodejs.rhel9] Failed to create snapshot object (name=openshift-4-23-base-image-20260529104357): ConflictError: 409

Test plan

  • New unit tests verify component name is included in the snapshot name
  • New unit test verifies the 63-char k8s DNS label limit is respected
  • New unit test verifies different components produce different names at the same timestamp
  • All existing tests pass (make test)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Snapshot and release names now include the relevant component name when available.
    • Generated Kubernetes names remain within the 63-character limit and avoid trailing or duplicate separators.
    • Releases retain the existing generic naming format when no component name is provided.
  • Tests

    • Added coverage for component-specific names, length limits, separator handling, and distinct names across components.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 685c9532-2067-492e-bbc4-2893ad8caf3b

📥 Commits

Reviewing files that changed from the base of the PR and between 1bd6b13 and d105afd.

📒 Files selected for processing (2)
  • doozer/doozerlib/backend/base_image_handler.py
  • doozer/tests/backend/test_base_image_handler.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • doozer/doozerlib/backend/base_image_handler.py
  • doozer/tests/backend/test_base_image_handler.py

Walkthrough

The base image handler now includes component names in snapshot and release resource names. It truncates names to Kubernetes limits, removes trailing separators, and preserves the generic release prefix when no component name is provided.

Changes

Component-aware resource naming

Layer / File(s) Summary
Snapshot name generation
doozer/doozerlib/backend/base_image_handler.py, doozer/tests/backend/test_base_image_handler.py
Snapshot names include the component name, stay within 63 characters, avoid trailing or duplicate separators, and differ across components.
Release name propagation
doozer/doozerlib/backend/base_image_handler.py, doozer/tests/backend/test_base_image_handler.py
Release creation accepts the component name and includes it in generateName when provided. The generic prefix remains when no component name is supplied.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 10 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Ai-Attribution ⚠️ Warning The PR description names Claude Code, while the tip commit uses Co-Authored-By for Claude and has no Assisted-by or Generated-by trailer. Replace the AI Co-Authored-By trailer with the required Assisted-by or Generated-by Red Hat attribution trailer.
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: including the component name in base-image snapshot names.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No-Weak-Crypto ✅ Passed The pull request contains no weak cryptography usage. Changes are limited to snapshot naming logic using string operations, not cryptographic operations.
Container-Privileges ✅ Passed The PR changes only Python orchestration and unit tests. Added Snapshot and Release objects contain no privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or container security settings.
No-Sensitive-Data-In-Logs ✅ Passed No new logging of sensitive data introduced. The PR adds component-name-based snapshot naming and truncation; no new logging statements were added. The existing logger.debug() of release_obj (conta...
No-Hardcoded-Secrets ✅ Passed The two changed files contain no API keys, tokens, passwords, private keys, embedded-credential URLs, sensitive string assignments, or long base64-like literals.
No-Injection-Vectors ✅ Passed No injection vectors detected. Code uses safe string formatting for Kubernetes names without SQL, shell, pickle, yaml.load, eval, or eval-like operations. Component names are normalized and truncat...
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@lgarciaaco lgarciaaco left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider renaming the release with a similar pattern

Maybe we want ta util method to derive resource names from group / comp / . We use a similar name convention for ec with generateName: "{application_name}-ec-{component_name}-"

# when multiple images are snapshotted within the same second.
# Format: {group_safe}-{comp_safe}-{timestamp}
# Total must fit within the 63-char Kubernetes DNS label limit.
comp_name = component["name"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is easy to ready we don't need comment overhead

@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 7, 2026
@lgarciaaco

lgarciaaco commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

/retitel ART-21851: Include component name in base-image snapshot name

@lgarciaaco

lgarciaaco commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

/retitle ART-21851: Include component name in base-image snapshot name

@openshift-ci openshift-ci Bot changed the title Include component name in base-image snapshot name ART-21851: Include component name in base-image snapshot name Aug 4, 2026
@openshift-ci-robot

openshift-ci-robot commented Aug 4, 2026

Copy link
Copy Markdown

@joepvd: This pull request references ART-21851 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Summary

  • When multiple base images were snapshotted within the same second, they got identical snapshot names ({group}-base-image-{timestamp}), causing 409 Conflict errors from the Kubernetes API
  • Include the Konflux component name in the snapshot name ({group}-{component}-{timestamp}) so each image gets a unique name even at second-level timestamp granularity
  • Component names are already k8s DNS label safe; only truncation is needed to stay within the 63-char limit

Example failure:

2026-05-29 10:43:57,111 doozerlib.backend.konflux_client INFO Creating appstudio.redhat.com/v1alpha1/Snapshot ocp-art-tenant/openshift-4-23-base-image-20260529104357...
2026-05-29 10:43:57,183 doozerlib.backend.konflux_client INFO Created appstudio.redhat.com/v1alpha1/Snapshot ocp-art-tenant/openshift-4-23-base-image-20260529104357
2026-05-29 10:43:57,183 artcommonlib INFO [containers/openshift-base-rhel9] Created snapshot openshift-4-23-base-image-20260529104357
2026-05-29 10:43:57,183 doozerlib.backend.konflux_client INFO Creating appstudio.redhat.com/v1alpha1/Snapshot ocp-art-tenant/openshift-4-23-base-image-20260529104357...
2026-05-29 10:43:57,237 artcommonlib ERROR [containers/openshift-base-nodejs.rhel9] Failed to create snapshot object (name=openshift-4-23-base-image-20260529104357): ConflictError: 409

Test plan

  • New unit tests verify component name is included in the snapshot name
  • New unit test verifies the 63-char k8s DNS label limit is respected
  • New unit test verifies different components produce different names at the same timestamp
  • All existing tests pass (make test)

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Aug 4, 2026
@joepvd
joepvd force-pushed the fix-snapshot-name-collision branch from 581f176 to 1bd6b13 Compare August 4, 2026 10:50
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 4, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
doozer/doozerlib/backend/base_image_handler.py (1)

167-167: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert component-name propagation in the caller test.

Line 167 is the contract that sends the Snapshot component name into Release name generation. The existing snapshot_release tests mock _create_release_from_snapshot but do not verify its third argument. Assert that this argument equals the component name passed to _snapshot_from_component.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doozer/doozerlib/backend/base_image_handler.py` at line 167, Update the
snapshot_release caller tests to assert that the mocked
_create_release_from_snapshot receives the component name passed to
_snapshot_from_component as its third argument, preserving the existing
assertions and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@doozer/doozerlib/backend/base_image_handler.py`:
- Around line 244-245: Update the component-name construction around
_truncate_for_k8s_name so truncating long component names preserves uniqueness
by appending a stable short hash derived from the full component["name"] within
the 63-character Kubernetes name budget. Keep snapshot_name within the existing
group/timestamp format, and add a regression test covering long names identical
through the truncation boundary but differing afterward.
- Around line 244-245: Update the snapshot-name construction around
_truncate_for_k8s_name by storing the calculated component length in
component_max_len, rejecting any value below 1 before truncating or constructing
snapshot_name, and preserving the 63-character Kubernetes name limit for valid
budgets.
- Around line 29-31: Update the short-name branch in the relevant
name-normalization function to return name.rstrip("-") instead of name, ensuring
trailing dashes are removed whether or not truncation occurs. Preserve the
existing truncation and trailing-dash stripping behavior for names exceeding
max_len.
- Around line 328-331: Update the generate_name construction in the
release_metadata flow to keep the Kubernetes prefix within 58 characters:
calculate the component budget with 58 - len(group_safe) - 2, constrain the
fallback group_safe-base-image-release- prefix to 58 characters, and add
boundary tests covering both component and fallback naming.

---

Nitpick comments:
In `@doozer/doozerlib/backend/base_image_handler.py`:
- Line 167: Update the snapshot_release caller tests to assert that the mocked
_create_release_from_snapshot receives the component name passed to
_snapshot_from_component as its third argument, preserving the existing
assertions and behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: aa089ed6-28d2-4267-b39f-ac4f9f1a9b95

📥 Commits

Reviewing files that changed from the base of the PR and between 344d8c0 and 1bd6b13.

📒 Files selected for processing (2)
  • doozer/doozerlib/backend/base_image_handler.py
  • doozer/tests/backend/test_base_image_handler.py

Comment on lines +29 to +31
if len(name) <= max_len:
return name
return name[:max_len].rstrip('-')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize trailing dashes on every path.

Line 30 returns name unchanged when it already fits. This bypasses the documented stripping any trailing dash behavior. A short component such as foo- produces group-foo--timestamp for snapshots and group-foo-- for release generateName. Return name.rstrip("-") in the short-name branch.

Suggested fix
     if len(name) <= max_len:
-        return name
+        return name.rstrip("-")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if len(name) <= max_len:
return name
return name[:max_len].rstrip('-')
if len(name) <= max_len:
return name.rstrip("-")
return name[:max_len].rstrip('-')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doozer/doozerlib/backend/base_image_handler.py` around lines 29 - 31, Update
the short-name branch in the relevant name-normalization function to return
name.rstrip("-") instead of name, ensuring trailing dashes are removed whether
or not truncation occurs. Preserve the existing truncation and trailing-dash
stripping behavior for names exceeding max_len.

Comment on lines +244 to +245
comp_name = _truncate_for_k8s_name(component["name"], 63 - len(group_safe) - len(timestamp) - 2)
snapshot_name = f"{group_safe}-{comp_name}-{timestamp}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve uniqueness after component truncation.

Line 244 keeps only the leading part of a long component name. Two distinct names that share that prefix then produce the same metadata.name when the group and timestamp match. For example, two names beginning with more than 33 identical characters collide with the current test values. Kubernetes requires names to be unique within a namespace. (kubernetes.io)

Append a stable short hash of the full component name within the available budget. Add a regression test with long names that differ only after the truncation boundary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doozer/doozerlib/backend/base_image_handler.py` around lines 244 - 245,
Update the component-name construction around _truncate_for_k8s_name so
truncating long component names preserves uniqueness by appending a stable short
hash derived from the full component["name"] within the 63-character Kubernetes
name budget. Keep snapshot_name within the existing group/timestamp format, and
add a regression test covering long names identical through the truncation
boundary but differing afterward.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject a non-positive snapshot-name budget.

The expression at Line 244 can return zero or a negative value. With a negative value, name[:max_len] removes characters from the end instead of limiting the result. The final Snapshot name can therefore exceed the 63-character contract. Compute component_max_len, reject values below 1, and fail before constructing the name.

Suggested guard
             timestamp = get_utc_now_formatted_str()
-            comp_name = _truncate_for_k8s_name(component["name"], 63 - len(group_safe) - len(timestamp) - 2)
+            component_max_len = 63 - len(group_safe) - len(timestamp) - 2
+            if component_max_len < 1:
+                raise ValueError("No room remains for the component name")
+            comp_name = _truncate_for_k8s_name(component["name"], component_max_len)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doozer/doozerlib/backend/base_image_handler.py` around lines 244 - 245,
Update the snapshot-name construction around _truncate_for_k8s_name by storing
the calculated component length in component_max_len, rejecting any value below
1 before truncating or constructing snapshot_name, and preserving the
63-character Kubernetes name limit for valid budgets.

Comment on lines +328 to +331
comp_safe = _truncate_for_k8s_name(component_name, 248 - len(group_safe) - 1) if component_name else ""
generate_name = f"{group_safe}-{comp_safe}-" if comp_safe else f"{group_safe}-base-image-release-"
release_metadata = {
"generateName": f"{group_safe}-base-image-release-",
"generateName": generate_name,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For the target Kubernetes API server version, what are the ObjectMeta.generateName maximum prefix length and generated-suffix truncation rules?

💡 Result:

In Kubernetes, the maximum length for an object name is 63 characters [1][2][3]. When using the metadata.generateName field, the Kubernetes API server appends a unique random suffix to the provided prefix to ensure the final name is unique [4][5][6]. The standard implementation for name generation uses a 5-character random alphanumeric suffix [1][2][3]. To ensure the final name does not exceed the 63-character limit, the API server truncates the provided generateName prefix if it is too long [1][2][3]. The maximum allowed length for the generateName prefix is 58 characters (63 characters total - 5 characters for the suffix) [1][2][3]. If the provided generateName prefix exceeds this 58-character limit, the server truncates it to the first 58 characters before appending the 5-character suffix [1][2][3]. Summary of Rules: - Maximum total name length: 63 characters [1][2][3]. - Random suffix length: 5 characters [1][2][3]. - Maximum prefix (generateName) length: 58 characters [1][2][3]. - Truncation rule: If the prefix is longer than 58 characters, it is truncated to 58 characters before the 5-character suffix is appended [1][2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
file=$(git ls-files | rg '(^|/)doozer/doozerlib/backend/base_image_handler\.py$' | head -n1)
printf '%s\n' "$file"
wc -l "$file"
sed -n '285,350p' "$file"
printf '\nRelated helpers and tests:\n'
rg -n "_truncate_for_k8s_name|generateName|base-image-release|component_name|group_safe" doozer tests 2>/dev/null | head -n 160

Repository: openshift-eng/art-tools

Length of output: 21088


🌐 Web query:

Kubernetes ObjectMeta generateName max length 253 63 DNS subdomain truncation generated suffix official source

💡 Result:

In Kubernetes, the metadata.generateName field is subject to the same validation rules as the metadata.name field for a given resource [1][2]. Because Kubernetes validates the length of the provided generateName prefix before appending the unique random suffix, the prefix must be short enough to allow for the suffix without exceeding the maximum allowed length for that resource's name [3]. For most Kubernetes resources that follow the RFC 1123 DNS Label standard (such as Pods), names are limited to 63 characters [4]. Since the server appends a random suffix (currently 10 characters long as of recent versions) [5], a generateName prefix that is 63 characters long will trigger a validation error, as the final name would exceed the limit [3]. To ensure compatibility and avoid validation failures, the generateName prefix should be kept under 58 characters (or more conservatively, to account for the current 10-character suffix, under 53 characters) [3][5]. For resources that follow the DNS Subdomain standard (which allows up to 253 characters), the generateName validation still enforces the stricter 63-character limit in many contexts, as the server-side validation logic is often shared or applied uniformly across resource metadata [3][4]. Key points regarding generateName truncation: - Validation occurs before suffix generation: Kubernetes validates the length of your provided generateName prefix against the resource's maximum allowed length (typically 63 characters) before the random suffix is added [3]. - Truncation: The server uses your provided generateName string as a prefix and appends a unique random suffix [1][4]. If the resulting name exceeds the length limit, the input is effectively truncated to accommodate the suffix, but providing a prefix that already meets or exceeds the maximum length causes an initial validation failure [3]. - Suffix length: While historically 5 characters, the random suffix was increased to 10 characters to reduce collision risks [5]. Relying on a specific suffix length is discouraged, as it is an internal implementation detail that may change [5]. Official documentation and API conventions confirm that generateName follows the same validation rules as Name and warns that it may be truncated to ensure uniqueness [1][6][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
file=doozer/doozerlib/backend/base_image_handler.py
printf '%s\n' 'Helper and snapshot naming logic:'
sed -n '1,45p' "$file"
sed -n '225,252p' "$file"
printf '%s\n' 'Relevant release tests:'
sed -n '180,235p' doozer/tests/backend/test_base_image_handler.py
sed -n '300,430p' doozer/tests/backend/test_base_image_handler.py
printf '%s\n' 'Release API and name validation references:'
rg -n "KIND_RELEASE|Release|generateName|253|248|DNS|name.*length" doozer/doozerlib/backend/base_image_handler.py doozer/tests/backend/test_base_image_handler.py doozer/doozerlib/backend/konflux_client.py | head -n 180

Repository: openshift-eng/art-tools

Length of output: 21059


🌐 Web query:

site:github.com/kubernetes/kubernetes generateName 10 random suffix 253 generate.go

💡 Result:

In the context of the Kubernetes codebase, the term generateName refers to a field in metadata that allows the server to automatically generate a unique name for an object by appending a random suffix to a provided base string [1]. This logic is implemented primarily within the file staging/src/k8s.io/apiserver/pkg/storage/names/generate.go [2][3]. The standard implementation, known as SimpleNameGenerator, takes a base string and appends a random suffix to ensure the resulting name is unique and does not exceed the maximum allowed length for a Kubernetes resource name, which is 63 characters [2][3]. Regarding the specific details mentioned in your query: 1. Random Suffix Length: While earlier versions of the Kubernetes API used a fixed 5-character random suffix [2][3], there have been discussions and updates aimed at adjusting this behavior to reduce collision risks [4][5]. For standard resource names, the total length is limited to 63 characters, meaning the base name is truncated if necessary to accommodate the suffix length [2][3]. 2. The value 253: This number typically refers to DNS1123SubdomainMaxLength (253 characters), which is the maximum length allowed for a full DNS-compliant subdomain name in Kubernetes, rather than the length of the generated resource name itself [6]. 3. generate.go: This is a common filename in the Kubernetes repository used for logic related to name generation [2][3] or, in other contexts like pkg/kubelet/status/generate.go, for generating status conditions for objects [7][8]. If you are encountering a specific error or logic flow involving 253, it is likely related to the validation of the full DNS subdomain path or an object metadata field that is subject to the 253-character limit, rather than the 63-character limit applied to individual resource names [6].

Citations:


🌐 Web query:

site:kubernetes.io/docs/reference/kubernetes-api/definitions/object-meta-v1-meta generateName may be truncated to accommodate the length limit

💡 Result:

In Kubernetes, the generateName field is used to generate a unique name for a resource when the Name field is not provided. If you specify a value for generateName, the server appends a random suffix to that value to ensure uniqueness. Because Kubernetes resource names have strict length constraints, the value provided in generateName may be truncated by the server to accommodate the length of this required unique suffix [1]. This ensures that the final generated name stays within the allowed maximum length for that specific resource type [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import json
from urllib.request import urlopen

urls = {
    "generator": "https://raw.githubusercontent.com/kubernetes/kubernetes/master/staging/src/k8s.io/apiserver/pkg/storage/names/generate.go",
    "objectmeta": "https://raw.githubusercontent.com/kubernetes/apimachinery/master/pkg/apis/meta/v1/validation/validation.go",
    "validation": "https://raw.githubusercontent.com/kubernetes/apimachinery/master/pkg/util/validation/validation.go",
}
for label, url in urls.items():
    print(f"\n--- {label}: {url} ---")
    try:
        text = urlopen(url, timeout=15).read().decode()
    except Exception as exc:
        print(type(exc).__name__, exc)
        continue
    for i, line in enumerate(text.splitlines(), 1):
        if any(term in line for term in (
            "NameRandomLength", "MaxGeneratedNameLength", "GenerateName",
            "ValidateObjectMeta", "NameIsDNS", "DNS1123SubdomainMaxLength",
            "TotalGeneratedNameLength",
        )):
            start = max(1, i - 4)
            end = min(len(text.splitlines()), i + 8)
            print("\n".join(f"{n}: {text.splitlines()[n-1]}" for n in range(start, end + 1)))
            print()
PY

Repository: openshift-eng/art-tools

Length of output: 983


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import re
from pathlib import Path
text = Path("doozer/doozerlib/backend/base_image_handler.py").read_text()
m = re.search(r"def _truncate_for_k8s_name.*?(?=\n\ndef )", text, re.S)
print(m.group(0) if m else "helper not found")
for group in ("openshift-4-22", "a" * 220, "a" * 240):
    budget = 248 - len(group) - 1
    comp = ("x" * 300)[:budget].rstrip("-")
    prefix = f"{group}-{comp}-"
    print(f"group={len(group)}, component_budget={budget}, prefix={len(prefix)}, fallback={len(group+'-base-image-release-')}")
PY

Repository: openshift-eng/art-tools

Length of output: 553


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' 'Kubernetes client/version constraints:'
rg -n "kubernetes|k8s|openshift" pyproject.toml setup.cfg setup.py requirements*.txt Pipfile* poetry.lock uv.lock 2>/dev/null | head -n 120 || true
printf '%s\n' 'Normalization implementation or vendored references:'
rg -n "def normalize_group_name_for_k8s|normalize_group_name_for_k8s" . --glob '!dist/**' --glob '!build/**' | head -n 80
printf '%s\n' 'CRD/API references for Release:'
rg -n "kind: Release|KIND_RELEASE|apiVersion:.*Release|Release/v1|appstudio.*Release" . --glob '*.yaml' --glob '*.yml' --glob '*.py' | head -n 140

Repository: openshift-eng/art-tools

Length of output: 8502


🏁 Script executed:

#!/bin/bash
set -eu
curl -ksSfL https://raw.githubusercontent.com/kubernetes/kubernetes/master/staging/src/k8s.io/apiserver/pkg/storage/names/generate.go |
  nl -ba | sed -n '1,140p'
printf '%s\n' '--- object metadata validation ---'
curl -ksSfL https://raw.githubusercontent.com/kubernetes/apimachinery/master/pkg/apis/meta/v1/validation/validation.go |
  nl -ba | rg -n -C 8 "ValidateObjectMeta|GenerateName"
printf '%s\n' '--- name validation constants ---'
curl -ksSfL https://raw.githubusercontent.com/kubernetes/apimachinery/master/pkg/util/validation/validation.go |
  nl -ba | rg -n -C 5 "DNS1123SubdomainMaxLength|DNS1123LabelMaxLength|NameIsDNS"

Repository: openshift-eng/art-tools

Length of output: 394


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '1198,1235p' artcommon/artcommonlib/util.py
sed -n '430,485p' artcommon/tests/test_util.py
printf '%s\n' 'Kubernetes generator source:'
curl -ksSfL https://raw.githubusercontent.com/kubernetes/kubernetes/master/staging/src/k8s.io/apiserver/pkg/storage/names/generate.go |
  awk 'BEGIN{n=0} {n++; if ($0 ~ /NameRandomLength|MaxGeneratedNameLength|GenerateName|TotalGeneratedNameLength/ || n<=90) print n ":" $0}'
printf '%s\n' 'Metadata validation source:'
curl -ksSfL https://raw.githubusercontent.com/kubernetes/apimachinery/master/pkg/apis/meta/v1/validation/validation.go |
  awk 'BEGIN{n=0} {n++; if ($0 ~ /ValidateObjectMeta|GenerateName/) print n ":" $0}'
printf '%s\n' 'Name validation constants:'
curl -ksSfL https://raw.githubusercontent.com/kubernetes/apimachinery/master/pkg/util/validation/validation.go |
  awk 'BEGIN{n=0} {n++; if ($0 ~ /DNS1123SubdomainMaxLength|DNS1123LabelMaxLength|NameIsDNS/) print n ":" $0}'

Repository: openshift-eng/art-tools

Length of output: 6726


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '1228,1248p' artcommon/artcommonlib/util.py
printf '%s\n' 'Kubernetes API-server name generation call sites:'
for url in \
  https://raw.githubusercontent.com/kubernetes/kubernetes/v1.29.0/staging/src/k8s.io/apiserver/pkg/storage/names/generate.go \
  https://raw.githubusercontent.com/kubernetes/kubernetes/v1.29.0/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go \
  https://raw.githubusercontent.com/kubernetes/kubernetes/v1.29.0/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go
do
  echo "--- $url ---"
  curl -ksSfL "$url" | grep -n -C 8 -E 'GenerateName|SimpleNameGenerator|NameGenerator' || true
done
printf '%s\n' 'Generated prefix lengths for the actual helper constraints:'
python3 - <<'PY'
suffix = "base-image-release-"
print(len(suffix), 44 + 1 + len(suffix))
for group_len in (14, 44):
    current_budget = 248 - group_len - 1
    current_prefix = group_len + 1 + current_budget + 1
    corrected_63_budget = 63 - 5 - group_len - 2
    print(group_len, current_budget, current_prefix, corrected_63_budget)
PY

Repository: openshift-eng/art-tools

Length of output: 5801


Limit the generateName prefix to 58 characters.

Kubernetes appends a five-character suffix and caps the generated name at 63 characters. The current 248 budget can cause server-side truncation and discard most of component_name.

Use 58 - len(group_safe) - 2 for the component budget, constrain the fallback prefix to 58 characters, and add boundary tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doozer/doozerlib/backend/base_image_handler.py` around lines 328 - 331,
Update the generate_name construction in the release_metadata flow to keep the
Kubernetes prefix within 58 characters: calculate the component budget with 58 -
len(group_safe) - 2, constrain the fallback group_safe-base-image-release-
prefix to 58 characters, and add boundary tests covering both component and
fallback naming.

When multiple base images were snapshotted within the same second, they
got the same snapshot name (group + timestamp), causing 409 Conflict
errors. Include the Konflux component name in the snapshot name to make
each one unique. Also add --ignore=build to pylint-imports to skip stale
build artifacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
@joepvd
joepvd force-pushed the fix-snapshot-name-collision branch from 1bd6b13 to d105afd Compare August 4, 2026 12:16
@lgarciaaco

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Aug 4, 2026
@openshift-ci

openshift-ci Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@joepvd: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/security d105afd link false /test security

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@fgallott

fgallott commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

/approve

@openshift-ci

openshift-ci Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: fgallott, lgarciaaco

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 4, 2026
@ashwindasr
ashwindasr merged commit 9cd31cb into openshift-eng:main Aug 4, 2026
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants