Skip to content

Add e2e test for ImageStream stale cross-namespace tag reference (OVP#443)#2313

Open
kaovilai wants to merge 1 commit into
openshift:oadp-devfrom
kaovilai:e2e-ovp-443-imagestream-stale-tag
Open

Add e2e test for ImageStream stale cross-namespace tag reference (OVP#443)#2313
kaovilai wants to merge 1 commit into
openshift:oadp-devfrom
kaovilai:e2e-ovp-443-imagestream-stale-tag

Conversation

@kaovilai

@kaovilai kaovilai commented Jul 17, 2026

Copy link
Copy Markdown
Member

Note

Responses generated with Claude

What this PR does

Adds an e2e regression test for openshift/openshift-velero-plugin#443: ImageStream backup failed with manifest unknown/500 when a tag was promoted cross-namespace and the source namespace's tag was later deleted/pruned. Fixed upstream by openshift/openshift-velero-plugin#444 (merged 2026-07-22).

Test flow (tests/e2e/imagestream_stale_reference_suite_test.go):

  1. An in-cluster oc new-build pushes a real image into a source namespace's ImageStream — the build's push (not an oc tag import) is what lands a managed image in the internal registry under that namespace's own repository.
  2. Promote into a target namespace with oc tag --source=docker <internal-src-ref> --reference --reference-policy=local — stores the tag event verbatim, skipping the apiserver's cross-namespace localize rewrite, deterministically reproducing the stale cross-namespace reference. A precondition assertion verifies the target's dockerImageReference still points at the source namespace's repository before proceeding.
  3. Delete the source tag and then the whole source namespace, so the source repository definitively no longer serves the digest.
  4. Back up the target namespace; assert it reaches Completed (not PartiallyFailed), and assert the backup logs show the image copy actually ran from the target stream's own repository (guards against a false green if image backup is disabled, and directly asserts the fixed source-path behavior).
  5. Restore into a fresh namespace and assert the ImageStream comes back with its tag history intact.

Also registers github.com/openshift/api/image/v1 in the shared e2e controller-runtime scheme (tests/e2e/lib/scheme.go).

Validation

This test was iterated in CI against both the unfixed and fixed plugin before landing on the final form:

  • Against the unfixed plugin: reproduced the exact bug — backup went PartiallyFailed, reading the copy source from the stale source-namespace repository (image-registry...svc:5000/imagestream-stale-ref-src/imagefoo@sha256:..., HTTP 500).
  • Against the fixed plugin (post OVP#444 merge, after the hourly quay.io/konveyor/openshift-velero-plugin:latest mirror caught up): backup completes, image copy log shows the target stream's own repository, and restore succeeds with tag history intact.

Test plan

  • go build ./... and go vet ./... pass for tests/e2e
  • CI green against the merged fix (openshift-velero-plugin#444)

@openshift-ci

openshift-ci Bot commented Jul 17, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci openshift-ci Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Jul 17, 2026
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds OpenShift ImageStream scheme registration, bounded oc execution, and an ordered end-to-end regression test for stale cross-namespace ImageStreamTag backup and restore behavior.

Changes

ImageStream stale-reference regression

Layer / File(s) Summary
ImageStream scheme and test helpers
tests/e2e/lib/scheme.go, tests/e2e/imagestream_stale_reference_suite_test.go
Registers ImageStream types, bounds oc commands to two minutes, and provides ImageStreamTag history lookup.
Stale-reference backup and restore flow
tests/e2e/imagestream_stale_reference_suite_test.go
Creates and promotes tags, removes the source namespace, validates backup outcomes and logs, and conditionally restores the target namespace.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant E2ESuite
  participant runOC
  participant KubernetesAPI
  participant Velero
  E2ESuite->>runOC: Create and promote ImageStreamTag
  runOC->>KubernetesAPI: Apply ImageStream resources
  E2ESuite->>runOC: Delete source tag and namespace
  E2ESuite->>Velero: Start target namespace backup
  Velero->>KubernetesAPI: Read target ImageStream data
  Velero-->>E2ESuite: Report backup phase and logs
  E2ESuite->>Velero: Restore target namespace when fix is merged
  Velero->>KubernetesAPI: Recreate ImageStream resources
  E2ESuite->>KubernetesAPI: Read restored TagEvent history
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (3 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning The backup-deletion Eventually swallows non-NotFound errors, so RBAC/apiserver failures retry until a generic timeout. Return the GetBackup error from the poll (or use a (bool,error) Eventually) so non-NotFound failures surface immediately.
Microshift Test Compatibility ⚠️ Warning The new Ginkgo suite uses build/image APIs via oc new-build, ImageStream, and ImageStreamTag, and it has no [Skipped:MicroShift]/apigroup tag or runtime skip. Add a MicroShift exclusion (preferred [apigroup:...] or [Skipped:MicroShift], or an exutil.IsMicroShiftCluster() g.Skip()) before running on MicroShift.
Ipv6 And Disconnected Network Test Compatibility ⚠️ Warning The new e2e test pulls registry.access.redhat.com/ubi9/ubi-micro:latest, requiring public registry access in disconnected IPv6 jobs. Use a mirrored/internal image (or skip with [Skipped:Disconnected]) and verify in an IPv6 disconnected CI job that no external fetches remain.
No-Sensitive-Data-In-Logs ❓ Inconclusive Investigating changed test and scheme files for logged sensitive values. Need code inspection of new logging paths to see whether any secrets/hostnames/PII are emitted.
✅ Passed checks (11 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Stable And Deterministic Test Names ✅ Passed The only Ginkgo title is a static Describe/It string; no titles embed generated names, dates, UUIDs, namespaces, or other run-specific values.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The new e2e test uses namespaces, builds, backup/restore, and ImageStream checks only; it has no multi-node scheduling or HA assumptions and no SNO skip is needed.
Topology-Aware Scheduling Compatibility ✅ Passed Only e2e test code and scheme registration changed; no manifests, controllers, replicas, affinity, node selectors, or PDBs were introduced.
Ote Binary Stdout Contract ✅ Passed No new stdout writes in process-level code; the added suite only uses stdlib log in Ginkgo hooks and scheme.go only registers APIs in init().
No-Weak-Crypto ✅ Passed Changed files only add ImageStream test logic and scheme registration; no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret comparisons found.
Container-Privileges ✅ Passed Only Go e2e test and scheme registration changed; no container/K8s manifests or privileged settings were added.
Title check ✅ Passed The title clearly and concisely summarizes the main change: an e2e test for stale cross-namespace ImageStream tag references.
Description check ✅ Passed The description covers why the PR exists and how it was tested, even though it uses different headings than the template.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 8

🧹 Nitpick comments (1)
tests/e2e/imagestream_stale_reference_suite_test.go (1)

60-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add diagnostic messages to the Gomega assertions.

Include the resource and operation in each failure message so CI output identifies which setup, backup, restore, or status check failed.

As per coding guidelines, “Ginkgo test assertions should include meaningful failure messages.”

Also applies to: 87-94, 103-117, 132-158

🤖 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 `@tests/e2e/imagestream_stale_reference_suite_test.go` around lines 60 - 70,
Add meaningful diagnostic messages to every Gomega assertion in the setup and
related checks, including the CreateOrUpdate error assertion and each Eventually
assertion in the referenced sections. Identify the specific resource and
operation—DPA reconciliation, Velero pod readiness, BSL availability, backup,
restore, and status checks—so failures clearly indicate what failed.

Source: Coding guidelines

🤖 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 `@tests/e2e/imagestream_stale_reference_suite_test.go`:
- Around line 42-43: Update the TODO comment in the stale reference end-to-end
test to reference openshift/openshift-velero-plugin#444 instead of `#443`,
preserving the existing instruction to un-pend the suite once the fix is merged
and vendored.
- Line 44: Add the [apigroup:image.openshift.io] label to the Ginkgo suite
declaration in PDescribe for the stale cross-namespace ImageStream reference
test, preserving its existing Ordered configuration and behavior.
- Around line 50-52: Replace the public registry image assigned to sourceImage
in the stale-reference suite with an image guaranteed to be available through
the CI mirror/internal registry; if no such image exists, mark the suite as
Skipped:Disconnected so disconnected runs do not attempt the pull.
- Around line 156-158: Update the restored-tag verification in the test to use a
bounded gomega.Eventually around getTagItems(targetNamespace, imageStreamName,
tagName), asserting the returned history becomes non-empty before the timeout.
Preserve the existing namespace, ImageStream name, and tag values, and retain
the current failure expectation once the polling deadline is reached.
- Around line 24-28: Update runOC to execute oc through exec.CommandContext with
a bounded timeout and ensure the context is properly cleaned up. In getTagItems,
distinguish Kubernetes NotFound errors from other client failures: preserve the
existing not-found handling, but return non-NotFound errors to the caller so
polling reports authentication or server failures.
- Around line 151-154: Update the Eventually callback in the restore completion
check to return both the success result and the error from
lib.IsRestoreCompletedSuccessfully instead of discarding the error. Preserve the
existing polling intervals and BeTrue assertion so terminal restore failures
propagate immediately.
- Around line 73-82: Update the ginkgo.AfterAll cleanup to assert that
DeleteNamespace succeeds for both sourceNamespace and targetNamespace instead of
discarding their errors, and delete the generated Backup and Restore custom
resources before completing teardown. Preserve the existing DPA deletion and
Velero wait behavior.

In `@tests/e2e/lib/scheme.go`:
- Line 32: Update init in tests/e2e/lib/scheme.go to handle the error returned
by openshiftimagev1.AddToScheme instead of discarding it. Use utilruntime.Must
or an equivalent explicit setup failure while preserving the existing scheme
registration behavior.

---

Nitpick comments:
In `@tests/e2e/imagestream_stale_reference_suite_test.go`:
- Around line 60-70: Add meaningful diagnostic messages to every Gomega
assertion in the setup and related checks, including the CreateOrUpdate error
assertion and each Eventually assertion in the referenced sections. Identify the
specific resource and operation—DPA reconciliation, Velero pod readiness, BSL
availability, backup, restore, and status checks—so failures clearly indicate
what failed.
🪄 Autofix (Beta)

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/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ace42979-3c62-41bc-ab1c-3cdbd32b2930

📥 Commits

Reviewing files that changed from the base of the PR and between f1a19db and a00bdd5.

📒 Files selected for processing (2)
  • tests/e2e/imagestream_stale_reference_suite_test.go
  • tests/e2e/lib/scheme.go

Comment thread tests/e2e/imagestream_stale_reference_suite_test.go
Comment thread tests/e2e/imagestream_stale_reference_suite_test.go Outdated
Comment thread tests/e2e/imagestream_stale_reference_suite_test.go Outdated
Comment thread tests/e2e/imagestream_stale_reference_suite_test.go Outdated
Comment thread tests/e2e/imagestream_stale_reference_suite_test.go
Comment thread tests/e2e/imagestream_stale_reference_suite_test.go Outdated
Comment thread tests/e2e/imagestream_stale_reference_suite_test.go Outdated
Comment thread tests/e2e/lib/scheme.go
@kaovilai

Copy link
Copy Markdown
Member Author

/cherry-pick oadp-1.6

@kaovilai

Copy link
Copy Markdown
Member Author

/cherry-pick oadp-1.5

@openshift-cherrypick-robot

Copy link
Copy Markdown
Contributor

@kaovilai: once the present PR merges, I will cherry-pick it on top of oadp-1.6 in a new PR and assign it to you.

Details

In response to this:

/cherry-pick oadp-1.6

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.

@openshift-cherrypick-robot

Copy link
Copy Markdown
Contributor

@kaovilai: once the present PR merges, I will cherry-pick it on top of oadp-1.5 in a new PR and assign it to you.

Details

In response to this:

/cherry-pick oadp-1.5

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.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/e2e/imagestream_stale_reference_suite_test.go (1)

71-71: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add failure messages to Expect(err).NotTo(HaveOccurred()) assertions.

Several assertions in this test omit a diagnostic message (e.g. Lines 75, 99, 107, 112, 145, 180, 191, 196, 211, 218, 223), so a failure only reports a generic error with no indication of which step failed. Lines 128, 158, 176 already follow the better pattern by attaching out.

As per coding guidelines, "Ginkgo test assertions should include meaningful failure messages to help diagnose what went wrong. Bad: Expect(err).NotTo(HaveOccurred()). Good: Expect(err).NotTo(HaveOccurred(), "failed to create test pod")."

🩹 Example fix (repeat pattern at the other unmessaged call sites)
-		err := dpaCR.CreateOrUpdate(dpaSpec)
-		gomega.Expect(err).NotTo(gomega.HaveOccurred())
+		err := dpaCR.CreateOrUpdate(dpaSpec)
+		gomega.Expect(err).NotTo(gomega.HaveOccurred(), "failed to create/update DPA")

Also applies to: 98-99, 106-113, 144-146, 179-180, 190-191, 195-197, 210-211, 217-224

🤖 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 `@tests/e2e/imagestream_stale_reference_suite_test.go` at line 71, Update every
unmessaged Expect(err).NotTo(HaveOccurred()) assertion in the test setup and
execution flows, including the call sites around lines 75, 99, 107, 112, 145,
180, 191, 196, 211, and 218–223, to pass a meaningful step-specific failure
message or the relevant out value. Preserve the existing assertions and follow
the already-message-bearing patterns near lines 128, 158, and 176.

Source: Coding guidelines

♻️ Duplicate comments (2)
tests/e2e/imagestream_stale_reference_suite_test.go (2)

54-67: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add [apigroup:image.openshift.io] to the suite title.

The suite exercises ImageStream/ImageStreamTag (an image.openshift.io API not available on MicroShift) but the PDescribe title still lacks the [apigroup:...] tag, so MicroShift-aware test selection can't exclude it once un-pended.

As per coding guidelines, "When MicroShift compatibility is needed, use [apigroup:...] tags (preferred), [Skipped:MicroShift] labels, or exutil.IsMicroShiftCluster() guards to skip tests on MicroShift."

🏷️ Proposed fix
-var _ = ginkgo.PDescribe("ImageStream backup with stale cross-namespace tag reference", ginkgo.Ordered, func() {
+var _ = ginkgo.PDescribe("ImageStream backup with stale cross-namespace tag reference [apigroup:image.openshift.io]", ginkgo.Ordered, func() {
🤖 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 `@tests/e2e/imagestream_stale_reference_suite_test.go` around lines 54 - 67,
Update the Ginkgo suite declaration containing PDescribe for “ImageStream backup
with stale cross-namespace tag reference” to include the
`[apigroup:image.openshift.io]` marker in its title, preserving the existing
Ordered configuration and test body.

Source: Coding guidelines


87-102: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Delete the generated Backup/Restore CRs in AfterAll.

Namespace-delete errors are now correctly surfaced (only NotFound is tolerated), but backupName/restoreName are still never cleaned up here, so every run leaves a Backup and Restore CR behind in namespace, accumulating across repeated executions.

log.Print("Deleting generated Backup/Restore CRs")
if backupName != "" {
    // delete the Backup CR named backupName in namespace, tolerating NotFound
}
if restoreName != "" {
    // delete the Restore CR named restoreName in namespace, tolerating NotFound
}
#!/bin/bash
# Check for existing helpers to delete Backup/Restore CRs used elsewhere in the e2e suite.
rg -n 'func Delete(Backup|Restore)\b' tests/e2e/lib
rg -n 'velerov1\.(Backup|Restore){}' tests/e2e -g '*.go' -A3 -B3
🤖 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 `@tests/e2e/imagestream_stale_reference_suite_test.go` around lines 87 - 102,
Extend the ginkgo.AfterAll cleanup to delete the generated Backup and Restore
CRs identified by backupName and restoreName in namespace, skipping empty names
and tolerating only NotFound errors. Reuse existing e2e deletion helpers or the
established Velero client patterns, and surface any other deletion failure
through the suite assertions before continuing with DPA cleanup.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In `@tests/e2e/imagestream_stale_reference_suite_test.go`:
- Line 71: Update every unmessaged Expect(err).NotTo(HaveOccurred()) assertion
in the test setup and execution flows, including the call sites around lines 75,
99, 107, 112, 145, 180, 191, 196, 211, and 218–223, to pass a meaningful
step-specific failure message or the relevant out value. Preserve the existing
assertions and follow the already-message-bearing patterns near lines 128, 158,
and 176.

---

Duplicate comments:
In `@tests/e2e/imagestream_stale_reference_suite_test.go`:
- Around line 54-67: Update the Ginkgo suite declaration containing PDescribe
for “ImageStream backup with stale cross-namespace tag reference” to include the
`[apigroup:image.openshift.io]` marker in its title, preserving the existing
Ordered configuration and test body.
- Around line 87-102: Extend the ginkgo.AfterAll cleanup to delete the generated
Backup and Restore CRs identified by backupName and restoreName in namespace,
skipping empty names and tolerating only NotFound errors. Reuse existing e2e
deletion helpers or the established Velero client patterns, and surface any
other deletion failure through the suite assertions before continuing with DPA
cleanup.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 2bc3a3e8-21ba-46ba-af3a-52e8412236ab

📥 Commits

Reviewing files that changed from the base of the PR and between 7180d85 and 47403f5.

📒 Files selected for processing (1)
  • tests/e2e/imagestream_stale_reference_suite_test.go

@kaovilai

Copy link
Copy Markdown
Member Author

/test all

@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

1 similar comment
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@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: 1

🤖 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 `@tests/e2e/imagestream_stale_reference_suite_test.go`:
- Around line 220-229: The backup-deletion poll around lib.GetBackup must
propagate errors other than apierrors.IsNotFound(err) instead of converting them
to false. Update the Eventually callback to return the encountered error
alongside its completion status, while preserving successful completion when the
backup is NotFound.
🪄 Autofix (Beta)

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/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f3703c13-602a-44c2-b7f8-4eccec979f60

📥 Commits

Reviewing files that changed from the base of the PR and between 6e1452b and 09c0ffe.

📒 Files selected for processing (1)
  • tests/e2e/imagestream_stale_reference_suite_test.go

Comment thread tests/e2e/imagestream_stale_reference_suite_test.go Outdated
kaovilai added a commit to kaovilai/openshift-velero-plugin that referenced this pull request Jul 22, 2026
Verbatim reference tags (`oc tag --reference`) produce status tag events
with an internal-registry DockerImageReference but an empty Image field:
openshift-apiserver's tagReferenceToTagEvent DockerImage branch sets only
DockerImageReference, and reference tags are never imported. Building the
copy source from the stream's own repository + digest would produce a
malformed "<repo>@" reference for such items and fail the backup.

There is nothing content-addressed to copy for these events, and the
imagestreamtag restore path restores reference tags as pointers without
needing image data, so skip them with a log line instead.

Surfaced by e2e work in openshift/oadp-operator#2313, which reproduces the
stale-reference scenario with a verbatim reference tag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
kaovilai added a commit to kaovilai/openshift-velero-plugin that referenced this pull request Jul 22, 2026
CopyLocalImageStreamImages built the copy source path from the status
item's DockerImageReference. When an image was promoted across
namespaces (e.g. `oc tag namespace-dev/imagefoo:v1
namespace-int/imagefoo:v1`), that reference can point at the source
namespace's repository, which stops serving the digest once the source
tag is deleted or pruned ("manifest unknown", or a 500 when the source
imagestream/namespace is gone entirely), failing the whole imagestream
backup item.

The digest is by definition a member of the imagestream being copied,
so build the source path from the stream's own namespace/name instead.
Same-namespace references produce byte-identical paths, and backup
(write to migration registry) and restore (read back) become symmetric.

Tag events with no image digest (verbatim reference tags from
`oc tag --reference`) are skipped with a log line: there is nothing
content-addressed to copy, and the imagestreamtag restore path restores
reference tags as pointers without needing image data. Building the
own-repository path for them would produce a malformed "<repo>@"
reference. Surfaced by e2e work in openshift/oadp-operator#2313.

Fixes openshift#443

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
kaovilai added a commit to kaovilai/openshift-velero-plugin that referenced this pull request Jul 22, 2026
CopyLocalImageStreamImages built the copy source path from the status
item's DockerImageReference. When an image was promoted across
namespaces (e.g. `oc tag namespace-dev/imagefoo:v1
namespace-int/imagefoo:v1`), that reference can point at the source
namespace's repository, which stops serving the digest once the source
tag is deleted or pruned ("manifest unknown", or a 500 when the source
imagestream/namespace is gone entirely), failing the whole imagestream
backup item.

The digest is by definition a member of the imagestream being copied,
so build the source path from the stream's own namespace/name instead.
Same-namespace references produce byte-identical paths, and backup
(write to migration registry) and restore (read back) become symmetric.

Tag events with no image digest (verbatim reference tags from
`oc tag --reference`) are skipped with a log line: there is nothing
content-addressed to copy, and the imagestreamtag restore path restores
reference tags as pointers without needing image data. Building the
own-repository path for them would produce a malformed "<repo>@"
reference. Surfaced by e2e work in openshift/oadp-operator#2313.

Fixes openshift#443

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
@kaovilai

Copy link
Copy Markdown
Member Author

/test 4.23-ci-index

Note

Responses generated with Claude

openshift-merge-bot Bot pushed a commit to openshift/openshift-velero-plugin that referenced this pull request Jul 22, 2026
…444)

CopyLocalImageStreamImages built the copy source path from the status
item's DockerImageReference. When an image was promoted across
namespaces (e.g. `oc tag namespace-dev/imagefoo:v1
namespace-int/imagefoo:v1`), that reference can point at the source
namespace's repository, which stops serving the digest once the source
tag is deleted or pruned ("manifest unknown", or a 500 when the source
imagestream/namespace is gone entirely), failing the whole imagestream
backup item.

The digest is by definition a member of the imagestream being copied,
so build the source path from the stream's own namespace/name instead.
Same-namespace references produce byte-identical paths, and backup
(write to migration registry) and restore (read back) become symmetric.

Tag events with no image digest (verbatim reference tags from
`oc tag --reference`) are skipped with a log line: there is nothing
content-addressed to copy, and the imagestreamtag restore path restores
reference tags as pointers without needing image data. Building the
own-repository path for them would produce a malformed "<repo>@"
reference. Surfaced by e2e work in openshift/oadp-operator#2313.

Fixes #443

Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@kaovilai
kaovilai force-pushed the e2e-ovp-443-imagestream-stale-tag branch from a89ea4b to 99e6913 Compare July 22, 2026 17:47
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@kaovilai
kaovilai marked this pull request as ready for review July 22, 2026 17:48
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 22, 2026
@openshift-ci
openshift-ci Bot requested review from eemcmullan and hhpatel14 July 22, 2026 17:52
@kaovilai
kaovilai force-pushed the e2e-ovp-443-imagestream-stale-tag branch from 99e6913 to 8a40419 Compare July 22, 2026 20:04
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@kaovilai
kaovilai force-pushed the e2e-ovp-443-imagestream-stale-tag branch from 8a40419 to 5922fa6 Compare July 22, 2026 22:25
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@kaovilai

Copy link
Copy Markdown
Member Author

/test 4.23-ci-index
/test 5.0-ci-index

Note

Responses generated with Claude

@kaovilai
kaovilai force-pushed the e2e-ovp-443-imagestream-stale-tag branch from 5922fa6 to db6714d Compare July 23, 2026 00:43
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@kaovilai

Copy link
Copy Markdown
Member Author

/test 4.23-e2e-test-aws
/test 4.22-e2e-test-cli-aws

Note

Responses generated with Claude

@kaovilai
kaovilai force-pushed the e2e-ovp-443-imagestream-stale-tag branch from db6714d to cc19916 Compare July 23, 2026 13:05
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@kaovilai

Copy link
Copy Markdown
Member Author

/test 4.23-ci-index

Note

Responses generated with Claude

…ference

Regression coverage for openshift-velero-plugin#443: backing up an
ImageStream whose tag references another namespace's repository in the
internal registry fails once that source repository's tag is deleted
or pruned, because the plugin built the image-copy source path from
the stale DockerImageReference instead of the stream's own repository.
Fixed upstream by openshift-velero-plugin#444 (merged 2026-07-22).

Test flow (tests/e2e/imagestream_stale_reference_suite_test.go):
1. An in-cluster `oc new-build` pushes a real image into a source
   namespace's ImageStream, landing it in the internal registry under
   that namespace's own repository (an external `oc tag` import would
   record the external pull spec instead, and the plugin would skip it
   entirely).
2. Create the target ImageStream directly via the API with a status
   tag entry copied from the source stream's resolved digest and
   DockerImageReference, reproducing the exact "tag references another
   namespace's repository" condition the bug depends on. This mirrors
   how openshift-velero-plugin's own restore path recreates ImageStream
   status (the only way a restored stream's tags are ever populated),
   and sidesteps `oc tag` entirely — see the iteration history below
   for why. No spec.tags entry is set: a cross-namespace
   Kind=ImageStreamImage spec.tags[].from is validated by the apiserver
   on every create (including the one Velero's restore performs), and
   fails once the source is gone — a separate, restore-time admission
   concern this test isn't targeting. The plugin's copy gate only reads
   status.tags, so spec.tags isn't needed to reproduce or fix the
   backup-time bug.
3. Delete the source tag and the whole source namespace so the source
   repository is definitively unavailable.
4. Back up the target namespace: assert it reaches Completed (not
   PartiallyFailed), and assert the backup logs show the image copy
   ran from the target stream's own repository.
5. Restore into a fresh namespace and confirm the ImageStream comes
   back with its tag history intact.

Also registers github.com/openshift/api/image/v1 in the shared e2e
controller-runtime scheme (tests/e2e/lib/scheme.go).

Validated in CI across six revisions of the arrange phase, each
diagnosed against openshift-velero-plugin's actual
CopyLocalImageStreamImages logic (velero-plugins/imagecopy/imagestream.go):
- Against the unfixed plugin, reproduced PartiallyFailed with the
  plugin reading from the stale source-namespace repository.
- `oc tag --source=docker --reference`: the `--reference` flag left the
  tag event's Image (digest) field empty. The plugin's only hard skip
  condition is exactly `len(tag.Items[i].Image) == 0` ("no image
  digest, reference tag") — Kind doesn't gate the copy at all, only
  whether the copied image also gets a destination :tag label. Copy
  never attempted; false green.
- Plain `oc tag src/x:tag dst/x:tag --reference-policy=local` (no
  --source=docker): resolved the digest correctly, but on the test
  cluster the apiserver normalized DockerImageReference to the
  destination's own repository, so the precondition never held.
- `oc tag --source=docker` without `--reference`: resolves the digest
  and retains the stale path in principle, but depends on the image
  import controller completing an async import of an internal-registry
  reference — this never resolved within 5 minutes, identically across
  all three OCP lanes tested in one run, ruling out a one-off flake.
- Constructing the target ImageStream directly via the API sidesteps
  oc-tag/import-controller behavior and reliably reproduces the backup-
  time bug; against the fixed plugin, the backup completes with a
  confirmed copy from the target stream's own repository. An interim
  revision also set a matching spec.tags[].from (Kind=ImageStreamImage,
  cross-namespace) for realism, but that entry fails apiserver
  validation on restore once the source is gone (a separate, restore-
  time admission concern) — dropped, since it isn't needed for the
  plugin's copy gate.

Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
@kaovilai
kaovilai force-pushed the e2e-ovp-443-imagestream-stale-tag branch from cc19916 to a604ddf Compare July 23, 2026 15:40
@kaovilai

Copy link
Copy Markdown
Member Author

/test all

Note

Responses generated with Claude

@kaovilai

Copy link
Copy Markdown
Member Author

/test 4.22-ci-index
/test 4.22-e2e-test-aws
/test 4.23-e2e-test-aws

Note

Responses generated with Claude

@kaovilai

Copy link
Copy Markdown
Member Author

/test 5.0-e2e-test-cli-aws

Note

Responses generated with Claude

@openshift-ci

openshift-ci Bot commented Jul 23, 2026

Copy link
Copy Markdown

@kaovilai: all tests passed!

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.

@openshift-ci

openshift-ci Bot commented Jul 23, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: kaovilai, shubham-pampattiwar

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-merge-bot Bot pushed a commit to openshift/openshift-velero-plugin that referenced this pull request Jul 23, 2026
…452)

CopyLocalImageStreamImages built the copy source path from the status
item's DockerImageReference. When an image was promoted across
namespaces (e.g. `oc tag namespace-dev/imagefoo:v1
namespace-int/imagefoo:v1`), that reference can point at the source
namespace's repository, which stops serving the digest once the source
tag is deleted or pruned ("manifest unknown", or a 500 when the source
imagestream/namespace is gone entirely), failing the whole imagestream
backup item.

The digest is by definition a member of the imagestream being copied,
so build the source path from the stream's own namespace/name instead.
Same-namespace references produce byte-identical paths, and backup
(write to migration registry) and restore (read back) become symmetric.

Tag events with no image digest (verbatim reference tags from
`oc tag --reference`) are skipped with a log line: there is nothing
content-addressed to copy, and the imagestreamtag restore path restores
reference tags as pointers without needing image data. Building the
own-repository path for them would produce a malformed "<repo>@"
reference. Surfaced by e2e work in openshift/oadp-operator#2313.

Fixes #443

Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Co-authored-by: Tiger Kaovilai <tkaovila@redhat.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants