Skip to content

feat: remove RekorSearchUI and migrate to Console CR - #2170

Merged
osmman merged 1 commit into
mainfrom
tturek/SECURESIGN-5066
Aug 5, 2026
Merged

feat: remove RekorSearchUI and migrate to Console CR#2170
osmman merged 1 commit into
mainfrom
tturek/SECURESIGN-5066

Conversation

@osmman

@osmman osmman commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Why

The RekorSearchUI component was embedded directly in the Rekor CR spec (v1alpha1 only). With the introduction of the standalone Console CR, SearchUI functionality is superseded. Existing v1alpha1 users need a migration path that preserves their SearchUI configuration as a Console CR, and orphaned SearchUI resources must be cleaned up.

What

Migration path — a shared internal/migration package provides annotation-based helpers (Set, Read, Pop, Propagate, Remove) for preserving v1alpha1-only fields through the conversion webhook. The v1alpha1→v1 converter (ConvertTo) serializes RekorSearchUI into a migration annotation; the Rekor controller's new migrationAction reads it, creates a Console CR when Enabled=true, and removes the annotation.

Cleanup — a new cleanupAction deletes orphaned SearchUI resources (Deployment, Service, Ingress, ServiceAccount, RoleBinding) owned by the Rekor CR and removes the stale UiAvailable status condition. Transient API errors cause a requeue rather than best-effort continuation.

v1 API removalRekorSearchUI field, image constant (RekorSearchUi), kustomize replacement block, and all SearchUI controller actions are removed from the v1 API surface.

Key design decisions:

  • Converter remains unconditional (migration.Set always fires) — it faithfully preserves v1alpha1 data without content-aware logic; the migration action decides behavior based on Enabled
  • ensure_rekor uses JSON Patch to surgically remove the migration annotation from Securesign, avoiding full-object update conflicts
  • Migration action uses kubernetes.CreateOrUpdate for Console CR creation, which handles retry on Conflict/AlreadyExists
  • Console CR is created as a top-level resource (no owner reference) since it has its own independent controller

Test coverage:

  • Unit tests for migration action (5 cases) and cleanup action (5 cases)
  • Integration tests via envtest for Console CR creation and orphaned resource cleanup
  • Conversion roundtrip fuzz tests updated with HubAfterMutation
  • e2e upgrade test verifies Console CR migration and old resource removal

Breaking changes

The spec.rekorSearchUI field is removed from the v1 Rekor CRD. Users on v1alpha1 with SearchUI enabled will have a Console CR automatically created on upgrade. Users on v1 are unaffected — SearchUI was never part of the v1 API.

Refs: SECURESIGN-5066

@qodo-for-securesign

qodo-for-securesign Bot commented Jul 29, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Remove RekorSearchUI and migrate v1alpha1 config to Console CR

✨ Enhancement 🐞 Bug fix 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Preserve v1alpha1 RekorSearchUI via migration annotations during v1alpha1↔v1 conversion.
• Rekor controller migrates enabled SearchUI to a Console CR and deletes orphaned UI resources.
• Remove SearchUI from the v1 API/CRDs/images and update unit/envtest/e2e coverage.
Diagram

graph TD
  A["v1alpha1 Rekor/Securesign"] --> B["Conversion webhook"] --> C["v1 Rekor/Securesign (migration ann)"] --> D["Securesign controller"] --> E["Rekor controller"]
  E --> F["Console CR"]
  E --> G["Delete legacy UI objs"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Deprecated passthrough field in v1 Rekor (keep rekorSearchUI)
  • ➕ Simplifies upgrades by avoiding an extra CR creation step
  • ➕ No migration annotations or cleanup flow needed
  • ➖ Keeps a deprecated API surface indefinitely
  • ➖ Conflicts with Console CR as the dedicated UI abstraction/controller boundary
2. One-time upgrade job/command (out-of-band migration)
  • ➕ Keeps controllers simpler in steady state
  • ➕ Can be run explicitly with controlled ordering
  • ➖ Operationally brittle across different upgrade mechanisms (OLM/GitOps)
  • ➖ Harder to guarantee execution and idempotency across partial upgrades
3. Rely only on conversion-data annotation (no dedicated migration domain)
  • ➕ Avoids introducing a second annotation namespace
  • ➕ Keeps state centralized in an existing conversion mechanism
  • ➖ Conversion-data is intended for roundtrip fidelity, not controller-driven work queues
  • ➖ Harder to precisely strip/propagate only the migration payload without side effects

Recommendation: The PR’s approach (dedicated migration annotations + idempotent controller actions) best matches Kubernetes upgrade realities: conversion remains unconditional and faithful, while controllers decide behavior based on Enabled and can reliably clean up legacy resources. The cleanup action and explicit annotation removal reduce repeated work and long-lived drift.

Files changed (40) +1168 / -2416

Enhancement (6) +289 / -36
rekor_conversion.goPreserve RekorSearchUI via migration annotation in Rekor conversion +21/-25

Preserve RekorSearchUI via migration annotation in Rekor conversion

• Introduces a stable migration key for v1alpha1 RekorSearchUI. ConvertTo serializes RekorSearchUI into a migration annotation; ConvertFrom pops it back into the spoke before MarshalData.

api/v1alpha1/rekor_conversion.go

securesign_conversion.goPreserve nested RekorSearchUI in Securesign conversion +14/-0

Preserve nested RekorSearchUI in Securesign conversion

• Serializes Securesign.spec.rekor.rekorSearchUI into the migration annotation in ConvertTo and restores it in ConvertFrom via Pop, ensuring it doesn't leak into conversion-data.

api/v1alpha1/securesign_conversion.go

migration.goAdd migration action to create Console CR from RekorSearchUI annotation +102/-0

Add migration action to create Console CR from RekorSearchUI annotation

• Reads RekorSearchUI JSON from the migration annotation; when Enabled=true, creates/updates a Console CR and maps PodRequirements/Ingress fields and Rekor reference. Removes the migration annotation from Rekor after successful processing.

internal/controller/rekor/actions/ui/migration.go

rekor_controller.goRemove SearchUI reconcile steps; add migration+cleanup into pipeline +4/-10

Remove SearchUI reconcile steps; add migration+cleanup into pipeline

• Removes legacy UI actions (deploy/service/ingress/status/rollout/RBAC) from Rekor reconciliation. Adds migration and cleanup actions early in the pipeline and grants RBAC to create/update Console CRs.

internal/controller/rekor/rekor_controller.go

ensure_rekor.goPropagate migration annotation to Rekor and patch it off Securesign +13/-1

Propagate migration annotation to Rekor and patch it off Securesign

• Ensures Securesign reconciliation forwards the migration annotation to the child Rekor resource. Removes the migration annotation from Securesign via patch after propagation to avoid repeated migrations.

internal/controller/securesign/actions/ensure_rekor.go

migration.goIntroduce shared migration annotation helper package +135/-0

Introduce shared migration annotation helper package

• Adds helpers for Key/Set/Read/Pop/Has/Remove and StripAll plus a Propagate helper for ensure-style annotation forwarding between parent and child CRs.

internal/migration/migration.go

Bug fix (1) +92 / -0
cleanup.goAdd cleanup action to delete orphaned SearchUI resources +92/-0

Add cleanup action to delete orphaned SearchUI resources

• Introduces a Rekor action that deletes legacy SearchUI resources owned by the Rekor CR (Deployment/Service/Ingress/ServiceAccount/RoleBinding) and removes the UiAvailable status condition. Treats API errors as reconcile errors (requeue) rather than best-effort continuation.

internal/controller/rekor/actions/ui/cleanup.go

Refactor (4) +14 / -101
rekor_types.goRemove RekorSearchUI from v1 Rekor API types +7/-21

Remove RekorSearchUI from v1 Rekor API types

• Deletes spec.rekorSearchUI and the RekorSearchUI type from the v1 Rekor API. Removes status.rekorSearchUIUrl from RekorStatus.

api/v1/rekor_types.go

zz_generated.deepcopy.goRegenerate deep-copy code after API type removals +0/-29

Regenerate deep-copy code after API type removals

• Updates generated deepcopy implementations to remove RekorSearchUI-related fields/types from v1 objects.

api/v1/zz_generated.deepcopy.go

zz_generated.conversion.goRegenerate conversions to remove RekorSearchUI peer conversions +7/-48

Regenerate conversions to remove RekorSearchUI peer conversions

• Removes generated conversion funcs for RekorSearchUI between v1 and v1alpha1 and updates generated RekorSpec conversions to flag RekorSearchUI as spoke-only requiring manual handling.

api/v1alpha1/zz_generated.conversion.go

constants.goRemove unused SearchUI constants +0/-3

Remove unused SearchUI constants

• Drops UI-only port/component constants while retaining names used for identifying legacy SearchUI resources for cleanup.

internal/controller/rekor/actions/constants.go

Tests (18) +752 / -156
rekor_types_test.goUpdate v1 Rekor type tests after SearchUI removal +0/-3

Update v1 Rekor type tests after SearchUI removal

• Adjusts tests to align with the updated v1 Rekor API schema (no RekorSearchUI fields).

api/v1/rekor_types_test.go

cleanup_test.goUnit test cleanup action behavior +159/-0

Unit test cleanup action behavior

• Adds tests ensuring the cleanup action triggers only when UiAvailable is present, deletes owned resources, skips unowned resources, and removes the condition.

internal/controller/rekor/actions/ui/cleanup_test.go

migration_test.goUnit test Console migration behavior +190/-0

Unit test Console migration behavior

• Covers Console creation, updating existing Console while preserving non-UI fields, and removal/skip behavior for empty/disabled/nil Enabled cases.

internal/controller/rekor/actions/ui/migration_test.go

rekor_controller_test.goUpdate Rekor controller tests for new reconcile graph +0/-13

Update Rekor controller tests for new reconcile graph

• Adjusts controller tests to match the removal of embedded SearchUI actions and the introduction of migration/cleanup behavior.

internal/controller/rekor/rekor_controller_test.go

rekor_hot_update_test.goUpdate hot-update tests after SearchUI removal +0/-3

Update hot-update tests after SearchUI removal

• Updates tests to avoid referencing removed SearchUI fields/actions in v1 Rekor reconciliation.

internal/controller/rekor/rekor_hot_update_test.go

rekor_migration_test.goEnvtest: verify Console creation and orphan cleanup from v1alpha1 Rekor +188/-0

Envtest: verify Console creation and orphan cleanup from v1alpha1 Rekor

• Adds envtest coverage that creating a v1alpha1 Rekor with RekorSearchUI enabled results in a Console CR and that the migration annotation is removed. Also verifies that a legacy SearchUI deployment owned by Rekor is deleted and UiAvailable is cleared.

internal/controller/rekor/rekor_migration_test.go

suite.goUpdate controller test suite wiring for new resources/actions +4/-4

Update controller test suite wiring for new resources/actions

• Adjusts test suite setup to align with new migration/cleanup behaviors and related scheme/controller wiring.

internal/controller/testonly/suite.go

migration_test.goAdd unit tests for migration helper semantics +176/-0

Add unit tests for migration helper semantics

• Validates JSON serialization, key generation, Pop vs Read behavior, StripAll, and Propagate copying behavior.

internal/migration/migration_test.go

rolling_upgrade_test.goUpdate rolling-upgrade e2e for Console migration expectations +0/-3

Update rolling-upgrade e2e for Console migration expectations

• Adjusts upgrade assertions to reflect that RekorSearchUI is migrated to Console and legacy UI artifacts should not remain after upgrade.

test/e2e/custom_install/rolling_upgrade_test.go

fips_test.goUpdate FIPS e2e expectations after SearchUI removal +0/-28

Update FIPS e2e expectations after SearchUI removal

• Removes/updates checks that relied on the embedded Rekor SearchUI component in v1.

test/e2e/fips/fips_test.go

common_ha_install_test.goRemove HA e2e checks for embedded SearchUI component +1/-35

Remove HA e2e checks for embedded SearchUI component

• Updates HA install validations to reflect removal of Rekor-managed SearchUI and align expected components with Console CR behavior.

test/e2e/high_avalability/common_ha_install_test.go

non_ha_to_ha_test.goUpdate non-HA→HA e2e upgrade assertions for Console migration +0/-13

Update non-HA→HA e2e upgrade assertions for Console migration

• Adjusts scenario expectations to no longer depend on RekorSearchUI being part of Rekor's reconcile-managed components.

test/e2e/high_avalability/non_ha_to_ha_test.go

common_install_test.goUpdate install e2e for v1 Rekor API change (no SearchUI) +0/-22

Update install e2e for v1 Rekor API change (no SearchUI)

• Removes assertions/setup that assumed Rekor v1 exposes or manages RekorSearchUI.

test/e2e/install/common_install_test.go

key_rotation_test.goRemove RekorSearchUI references from key-rotation lifecycle tests +0/-1

Remove RekorSearchUI references from key-rotation lifecycle tests

• Updates lifecycle coverage to avoid relying on removed UI status/resources.

test/e2e/lifecycle/key_rotation_test.go

search_ui.goUpdate Rekor SearchUI e2e support to reflect migration to Console +0/-16

Update Rekor SearchUI e2e support to reflect migration to Console

• Adjusts helper code used by e2e tests so SearchUI is no longer managed as part of Rekor v1 and can be validated via Console migration/cleanup behavior.

test/e2e/support/tas/rekor/search_ui.go

securesign.goUpdate Securesign e2e support for migration annotation propagation +0/-13

Update Securesign e2e support for migration annotation propagation

• Updates helper logic constructing/verifying Securesign/Rekor resources to align with the migration annotation propagation and Console CR creation behavior.

test/e2e/support/tas/securesign/securesign.go

suite_test.goUpdate e2e update suite after removing SearchUI test surface +0/-1

Update e2e update suite after removing SearchUI test surface

• Adjusts suite wiring/assumptions so the update test suite does not rely on the old RekorSearchUI behavior.

test/e2e/update/suite_test.go

upgrade_test.goAdd explicit upgrade verification for SearchUI→Console migration and cleanup +34/-1

Add explicit upgrade verification for SearchUI→Console migration and cleanup

• Adds an upgrade assertion that a Console CR is created (UI enabled), legacy SearchUI k8s resources are deleted, and Rekor UiAvailable is removed.

test/e2e/upgrade_test.go

Other (11) +21 / -2123
main.ymlRemove RekorSearchUI e2e wiring from CI workflow +1/-4

Remove RekorSearchUI e2e wiring from CI workflow

• Stops exporting REKOR_UI_URL from Rekor status and removes Playwright-driven UI test setup. Updates the e2e invocation to skip the rekorsearchui test package.

.github/workflows/main.yml

rekor_defaults.goDrop RekorSearchUI defaulting in v1 Rekor spec +0/-6

Drop RekorSearchUI defaulting in v1 Rekor spec

• Removes RekorSearchUI.SetDefaults() invocation and deletes the RekorSearchUI defaulting function, reflecting SearchUI removal from the v1 API.

api/v1/rekor_defaults.go

conversion_roundtrip_test.goMake fuzz roundtrips ignore migration annotations +17/-0

Make fuzz roundtrips ignore migration annotations

• Adds HubAfterMutation hooks to strip all migration annotations from hub objects so they don't break roundtrip expectations. Also normalizes v1alpha1-only RekorSearchUIUrl during fuzzing.

api/v1alpha1/conversion_roundtrip_test.go

conversion_unit_test.goAdjust conversion unit tests for migration annotations +3/-0

Adjust conversion unit tests for migration annotations

• Updates v1alpha1 conversion unit tests to reflect that RekorSearchUI is preserved via migration annotations rather than peer-type conversions in v1.

api/v1alpha1/conversion_unit_test.go

main.goRemove related image flag for Rekor Search UI +0/-1

Remove related image flag for Rekor Search UI

• Drops the CLI flag exposing the RekorSearchUI related image environment variable.

cmd/main.go

rhtas.redhat.com_rekors.yamlRemove rekorSearchUI from Rekor CRD schema +0/-1045

Remove rekorSearchUI from Rekor CRD schema

• Updates the Rekor CRD to remove spec.rekorSearchUI and status.rekorSearchUIUrl from the published v1 schema.

config/crd/bases/rhtas.redhat.com_rekors.yaml

rhtas.redhat.com_securesigns.yamlRegenerate Securesign CRD after migration changes +0/-1049

Regenerate Securesign CRD after migration changes

• Updates the Securesign CRD output to match the new conversion/migration behavior and the removal of embedded SearchUI from the v1 surface.

config/crd/bases/rhtas.redhat.com_securesigns.yaml

images.envRemove SearchUI related image from default image set +0/-3

Remove SearchUI related image from default image set

• Deletes the RELATED_IMAGE_REKOR_SEARCH_UI entry from the default images environment file.

config/default/images.env

kustomization.yamlRemove kustomize replacement for SearchUI related image +0/-11

Remove kustomize replacement for SearchUI related image

• Deletes the replacement block that injected RELATED_IMAGE_REKOR_SEARCH_UI into the manager deployment env.

config/default/kustomization.yaml

manager_images_patch.yamlRemove manager env wiring for SearchUI image +0/-2

Remove manager env wiring for SearchUI image

• Updates the manager images patch to drop the SearchUI image env var injection.

config/default/manager_images_patch.yaml

images.goRemove RekorSearchUi from image registry +0/-2

Remove RekorSearchUi from image registry

• Deletes the RekorSearchUi image constant and removes it from the operator's image list.

internal/images/images.go

@osmman
osmman force-pushed the tturek/SECURESIGN-5066 branch from 16e6158 to e63a37c Compare July 29, 2026 12:56
@qodo-for-securesign

qodo-for-securesign Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Paused reconcile drops migration 🐞 Bug ≡ Correctness ⭐ New
Description
In ensure_rekor.Handle, the SearchUI migration annotation is removed from Securesign even when Rekor
updates are skipped due to pause-reconciliation, so the migration payload may never be propagated to
the Rekor CR. This can permanently lose RekorSearchUI configuration (and block Console CR
creation/cleanup) during upgrade for paused Rekor instances.
Code

internal/controller/securesign/actions/ensure_rekor.go[R77-82]

+	if migration.Has(instance, v1alpha1.MigrationSearchUIData) {
+		before := instance.DeepCopy()
+		migration.Remove(instance, v1alpha1.MigrationSearchUIData)
+		if err := i.Client.Patch(ctx, instance, client.MergeFrom(before)); err != nil {
+			return i.Error(ctx, fmt.Errorf("could not remove migration annotation from Securesign: %w", err), instance)
+		}
Relevance

●●● Strong

Migration/upgrade correctness issues are typically fixed; data-loss risk for paused instances likely
accepted.

PR-#1723
PR-#1928

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
ensure_rekor propagates the migration annotation to the child Rekor via an ensure function passed
into kubernetes.CreateOrUpdate, but that wrapper intentionally skips all ensure/mutate functions
when the managed object is paused. Despite that, ensure_rekor then removes the annotation from
Securesign unconditionally. Since the Rekor controller also filters out paused objects, the payload
may never be available to the Rekor-side migration action, causing migration data loss.

internal/controller/securesign/actions/ensure_rekor.go[55-83]
internal/utils/kubernetes/common.go[115-132]
internal/controller/rekor/rekor_controller.go[175-185]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ensure_rekor` removes the migration annotation from the parent `Securesign` even in cases where the migration annotation was not successfully propagated to the child `Rekor` (e.g., because `CreateOrUpdate` skips all mutate/ensure functions when the target object is paused). This can drop the only remaining copy of the migration payload and prevent Rekor-side migration from ever running.

## Issue Context
- `kubernetes.CreateOrUpdate` exits early (skipping all provided ensure/mutate functions) when the managed object has `rhtas.redhat.com/pause-reconciliation=true`.
- `ensure_rekor` currently always removes the migration annotation from `Securesign` if present, regardless of whether it was actually copied onto `Rekor`.

## Fix Focus Areas
- internal/controller/securesign/actions/ensure_rekor.go[55-83]

### Suggested implementation direction
- Only remove `v1alpha1.MigrationSearchUIData` from `Securesign` after confirming the child `Rekor` has the annotation (either because it was just propagated, or it already existed).
- If the child `Rekor` is paused (or otherwise did not receive the annotation), leave the parent annotation intact and let a future reconcile attempt propagate it once unpaused.
- Add/extend a unit test to cover the paused-child scenario (Rekor has pause annotation; ensure_rekor should not remove migration annotation from Securesign unless propagation is confirmed).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Console spec overwritten ✓ Resolved 🐞 Bug ≡ Correctness
Description
migrationAction replaces the entire Console.Spec with a struct containing only UI fields, so an
existing Console with the same name will have Api, TrustedCA, and ServiceAccountConfig reset
to zero values. This can silently erase user Console configuration during migration.
Code

internal/controller/rekor/actions/ui/migration.go[R65-79]

+			object.Spec = rhtasv1.ConsoleSpec{
+				UI: rhtasv1.ConsoleUI{
+					PodRequirements: rhtasv1.PodRequirements{
+						Replicas:    searchUI.Replicas,
+						Affinity:    searchUI.Affinity,
+						Resources:   searchUI.Resources,
+						Tolerations: searchUI.Tolerations,
+					},
+					Ingress: rhtasv1.Ingress{
+						Enabled: searchUI.Enabled,
+						Host:    searchUI.Host,
+						Labels:  searchUI.RouteSelectorLabels,
+					},
+				},
+			}
Relevance

●●● Strong

Team prioritizes upgrade/migration safety; avoiding silent spec resets prevents user config loss.

PR-#1928

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The migration action explicitly assigns a new ConsoleSpec with only UI set; since ConsoleSpec
also includes Api, TrustedCA, and ServiceAccountConfig, those fields are cleared on update of
an existing Console.

internal/controller/rekor/actions/ui/migration.go[55-84]
api/v1/console_types.go[24-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Rekor SearchUI migration action uses `CreateOrUpdate` but assigns `object.Spec = rhtasv1.ConsoleSpec{ UI: ... }`, which overwrites *all* other Console spec fields if the Console already exists.

## Issue Context
`ConsoleSpec` contains more than UI settings (e.g., `Api`, `TrustedCA`, and inline `ServiceAccountConfig`). The migration should only set the migrated UI fields and leave unrelated user configuration intact.

## Fix Focus Areas
- internal/controller/rekor/actions/ui/migration.go[63-81]

## Implementation notes
- In the `CreateOrUpdate` mutate function, update only `object.Spec.UI` (and its nested fields) instead of reassigning `object.Spec`.
- If you need to set defaults for new objects, do so without clearing existing `object.Spec.Api`, `object.Spec.TrustedCA`, and `object.Spec.ServiceAccountConfig`.
- Add/extend a unit test where `existingConsole.Spec.Api` and/or `TrustedCA` are pre-set and assert they remain unchanged after migration.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Non-idempotent annotation removal ✓ Resolved 🐞 Bug ☼ Reliability
Description
ensure_rekor removes the migration annotation using a JSON Patch remove op, which fails if the
annotation path is already absent on the apiserver (e.g., stale cache or concurrent update). This
can turn an already-cleaned Securesign into transient reconcile errors and retries.
Code

internal/controller/securesign/actions/ensure_rekor.go[R80-92]

+	if migration.Has(instance, v1alpha1.MigrationSearchUIData) {
+		patch, err := json.Marshal([]map[string]string{
+			{
+				"op":   "remove",
+				"path": "/metadata/annotations/" + strings.ReplaceAll(v1alpha1.MigrationSearchUIData, "/", "~1"),
+			},
+		})
+		if err != nil {
+			return i.Error(ctx, fmt.Errorf("could not marshal annotation patch: %w", err), instance)
+		}
+		if err := i.Client.Patch(ctx, instance, client.RawPatch(types.JSONPatchType, patch)); err != nil {
+			return i.Error(ctx, fmt.Errorf("could not remove migration annotation from Securesign: %w", err), instance)
+		}
Relevance

●●● Strong

They commonly harden reconciles against transient API/state issues; idempotent annotation removal
avoids noisy retries.

PR-#1406

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code constructs a JSON Patch remove operation targeting the annotation key; migration.Has
only checks the local annotations map, so it can attempt a remove even if the live object no longer
has that path.

internal/controller/securesign/actions/ensure_rekor.go[80-94]
internal/migration/migration.go[83-89]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Securesign controller uses JSON Patch with `op: remove` to delete the migration annotation. JSON Patch remove is not idempotent: it errors if the path doesn’t exist at patch-apply time.

## Issue Context
Even if the in-memory object has the annotation, the live apiserver object may differ (e.g., another writer already removed it). In that case, the patch fails and reconciliation returns an error.

## Fix Focus Areas
- internal/controller/securesign/actions/ensure_rekor.go[80-94]
- internal/migration/migration.go[83-89]

## Implementation notes
- Prefer an idempotent patch strategy for annotation removal, e.g.:
 - Build a `before := instance.DeepCopy()`
 - `migration.Remove(instance, key)`
 - `i.Client.Patch(ctx, instance, client.MergeFrom(before))`
 This produces a JSON merge patch that removes the key (typically via `null`) and does not fail if the key is already absent.
- Alternatively, keep JSON Patch but treat “missing path” style errors as success (after re-GET verification), though merge patch is simpler and safer.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Previous review results

Review updated until commit d769fe5

Results up to commit 16e6158 ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Console spec overwritten ✓ Resolved 🐞 Bug ≡ Correctness
Description
migrationAction replaces the entire Console.Spec with a struct containing only UI fields, so an
existing Console with the same name will have Api, TrustedCA, and ServiceAccountConfig reset
to zero values. This can silently erase user Console configuration during migration.
Code

internal/controller/rekor/actions/ui/migration.go[R65-79]

+			object.Spec = rhtasv1.ConsoleSpec{
+				UI: rhtasv1.ConsoleUI{
+					PodRequirements: rhtasv1.PodRequirements{
+						Replicas:    searchUI.Replicas,
+						Affinity:    searchUI.Affinity,
+						Resources:   searchUI.Resources,
+						Tolerations: searchUI.Tolerations,
+					},
+					Ingress: rhtasv1.Ingress{
+						Enabled: searchUI.Enabled,
+						Host:    searchUI.Host,
+						Labels:  searchUI.RouteSelectorLabels,
+					},
+				},
+			}
Relevance

●●● Strong

Team prioritizes upgrade/migration safety; avoiding silent spec resets prevents user config loss.

PR-#1928

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The migration action explicitly assigns a new ConsoleSpec with only UI set; since ConsoleSpec
also includes Api, TrustedCA, and ServiceAccountConfig, those fields are cleared on update of
an existing Console.

internal/controller/rekor/actions/ui/migration.go[55-84]
api/v1/console_types.go[24-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Rekor SearchUI migration action uses `CreateOrUpdate` but assigns `object.Spec = rhtasv1.ConsoleSpec{ UI: ... }`, which overwrites *all* other Console spec fields if the Console already exists.

## Issue Context
`ConsoleSpec` contains more than UI settings (e.g., `Api`, `TrustedCA`, and inline `ServiceAccountConfig`). The migration should only set the migrated UI fields and leave unrelated user configuration intact.

## Fix Focus Areas
- internal/controller/rekor/actions/ui/migration.go[63-81]

## Implementation notes
- In the `CreateOrUpdate` mutate function, update only `object.Spec.UI` (and its nested fields) instead of reassigning `object.Spec`.
- If you need to set defaults for new objects, do so without clearing existing `object.Spec.Api`, `object.Spec.TrustedCA`, and `object.Spec.ServiceAccountConfig`.
- Add/extend a unit test where `existingConsole.Spec.Api` and/or `TrustedCA` are pre-set and assert they remain unchanged after migration.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Non-idempotent annotation removal ✓ Resolved 🐞 Bug ☼ Reliability
Description
ensure_rekor removes the migration annotation using a JSON Patch remove op, which fails if the
annotation path is already absent on the apiserver (e.g., stale cache or concurrent update). This
can turn an already-cleaned Securesign into transient reconcile errors and retries.
Code

internal/controller/securesign/actions/ensure_rekor.go[R80-92]

+	if migration.Has(instance, v1alpha1.MigrationSearchUIData) {
+		patch, err := json.Marshal([]map[string]string{
+			{
+				"op":   "remove",
+				"path": "/metadata/annotations/" + strings.ReplaceAll(v1alpha1.MigrationSearchUIData, "/", "~1"),
+			},
+		})
+		if err != nil {
+			return i.Error(ctx, fmt.Errorf("could not marshal annotation patch: %w", err), instance)
+		}
+		if err := i.Client.Patch(ctx, instance, client.RawPatch(types.JSONPatchType, patch)); err != nil {
+			return i.Error(ctx, fmt.Errorf("could not remove migration annotation from Securesign: %w", err), instance)
+		}
Relevance

●●● Strong

They commonly harden reconciles against transient API/state issues; idempotent annotation removal
avoids noisy retries.

PR-#1406

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code constructs a JSON Patch remove operation targeting the annotation key; migration.Has
only checks the local annotations map, so it can attempt a remove even if the live object no longer
has that path.

internal/controller/securesign/actions/ensure_rekor.go[80-94]
internal/migration/migration.go[83-89]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Securesign controller uses JSON Patch with `op: remove` to delete the migration annotation. JSON Patch remove is not idempotent: it errors if the path doesn’t exist at patch-apply time.

## Issue Context
Even if the in-memory object has the annotation, the live apiserver object may differ (e.g., another writer already removed it). In that case, the patch fails and reconciliation returns an error.

## Fix Focus Areas
- internal/controller/securesign/actions/ensure_rekor.go[80-94]
- internal/migration/migration.go[83-89]

## Implementation notes
- Prefer an idempotent patch strategy for annotation removal, e.g.:
 - Build a `before := instance.DeepCopy()`
 - `migration.Remove(instance, key)`
 - `i.Client.Patch(ctx, instance, client.MergeFrom(before))`
 This produces a JSON merge patch that removes the key (typically via `null`) and does not fail if the key is already absent.
- Alternatively, keep JSON Patch but treat “missing path” style errors as success (after re-GET verification), though merge patch is simpler and safer.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread internal/controller/rekor/actions/ui/migration.go Outdated
@codecov-commenter

codecov-commenter commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.00000% with 56 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.77%. Comparing base (4c2af50) to head (d769fe5).

Files with missing lines Patch % Lines
internal/migration/migration.go 71.69% 13 Missing and 2 partials ⚠️
internal/controller/rekor/actions/ui/migration.go 80.70% 6 Missing and 5 partials ⚠️
api/v1alpha1/rekor_conversion.go 44.44% 2 Missing and 3 partials ⚠️
api/v1alpha1/securesign_conversion.go 28.57% 2 Missing and 3 partials ⚠️
...rnal/controller/securesign/actions/ensure_rekor.go 16.66% 4 Missing and 1 partial ⚠️
api/v1alpha1/zz_generated.conversion.go 0.00% 3 Missing and 1 partial ⚠️
...ernal/controller/console/actions/api/deployment.go 0.00% 3 Missing ⚠️
internal/controller/rekor/actions/ui/cleanup.go 94.23% 2 Missing and 1 partial ⚠️
...nternal/controller/tuf/serviceresolver/resolver.go 40.00% 2 Missing and 1 partial ⚠️
internal/controller/testonly/suite.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2170      +/-   ##
==========================================
- Coverage   56.95%   56.77%   -0.19%     
==========================================
  Files         287      284       -3     
  Lines       16251    16153      -98     
==========================================
- Hits         9256     9171      -85     
+ Misses       6043     6032      -11     
+ Partials      952      950       -2     
Flag Coverage Δ
e2e 68.12% <13.60%> (-1.55%) ⬇️
unit 36.45% <68.50%> (+0.77%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@osmman
osmman force-pushed the tturek/SECURESIGN-5066 branch from e63a37c to 5324f1c Compare July 29, 2026 13:28
@osmman osmman added the enhancement New feature or request label Jul 29, 2026
@osmman
osmman force-pushed the tturek/SECURESIGN-5066 branch 5 times, most recently from a21102b to c9799f5 Compare July 29, 2026 20:18
@kdacosta0

Copy link
Copy Markdown
Member

Will need to do org wide cleanup of Rekor Search UI for >= 1.5.0 (e.g. sigstore-e2e, pipelines, quickstarts?)
Probably should add Jira tasks so we track it

@osmman

osmman commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Will need to do org wide cleanup of Rekor Search UI for >= 1.5.0 (e.g. sigstore-e2e, pipelines, quickstarts?) Probably should add Jira tasks so we track it

Yes I will create a jira to request change in sigstore-e2e for Console. I prepared securesign/pipelines#569 to exclude these tests for deployments without Search UI.

@osmman
osmman marked this pull request as draft July 30, 2026 09:12
@osmman

osmman commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Depends on #2149 to correctly configure Rekor ServiceReference during migration

@osmman
osmman force-pushed the tturek/SECURESIGN-5066 branch from c9799f5 to 3f12100 Compare August 4, 2026 07:54
@osmman
osmman marked this pull request as ready for review August 4, 2026 07:54
Comment on lines +77 to +82
if migration.Has(instance, v1alpha1.MigrationSearchUIData) {
before := instance.DeepCopy()
migration.Remove(instance, v1alpha1.MigrationSearchUIData)
if err := i.Client.Patch(ctx, instance, client.MergeFrom(before)); err != nil {
return i.Error(ctx, fmt.Errorf("could not remove migration annotation from Securesign: %w", err), instance)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Paused reconcile drops migration 🐞 Bug ≡ Correctness

In ensure_rekor.Handle, the SearchUI migration annotation is removed from Securesign even when Rekor
updates are skipped due to pause-reconciliation, so the migration payload may never be propagated to
the Rekor CR. This can permanently lose RekorSearchUI configuration (and block Console CR
creation/cleanup) during upgrade for paused Rekor instances.
Agent Prompt
## Issue description
`ensure_rekor` removes the migration annotation from the parent `Securesign` even in cases where the migration annotation was not successfully propagated to the child `Rekor` (e.g., because `CreateOrUpdate` skips all mutate/ensure functions when the target object is paused). This can drop the only remaining copy of the migration payload and prevent Rekor-side migration from ever running.

## Issue Context
- `kubernetes.CreateOrUpdate` exits early (skipping all provided ensure/mutate functions) when the managed object has `rhtas.redhat.com/pause-reconciliation=true`.
- `ensure_rekor` currently always removes the migration annotation from `Securesign` if present, regardless of whether it was actually copied onto `Rekor`.

## Fix Focus Areas
- internal/controller/securesign/actions/ensure_rekor.go[55-83]

### Suggested implementation direction
- Only remove `v1alpha1.MigrationSearchUIData` from `Securesign` after confirming the child `Rekor` has the annotation (either because it was just propagated, or it already existed).
- If the child `Rekor` is paused (or otherwise did not receive the annotation), leave the parent annotation intact and let a future reconcile attempt propagate it once unpaused.
- Add/extend a unit test to cover the paused-child scenario (Rekor has pause annotation; ensure_rekor should not remove migration annotation from Securesign unless propagation is confirmed).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-for-securesign

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 3f12100

Comment thread config/default/images.env
Namespace: instance.Namespace,
},
},
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could you please add TUF ref here as well ?

object.Spec.Api.Tuf = rhtasv1.ServiceReference{
				Ref: tufRef,
}

It's always better to have the TUF public public URL (if possible) instead of the internal one.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I let to resolved it by autodiscovery (ResolveExternalServiceUrl) in Console controller. Rekor have not got any usefull data from which I can extract value.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@fghanmi is TUF url mandatory for API server or optional. Because by enabling autodiscovery and removing your old fallback I make it mandatory.

@osmman
osmman force-pushed the tturek/SECURESIGN-5066 branch from 3f12100 to 69a62c7 Compare August 5, 2026 09:11
}
} else {
tufURL = fmt.Sprintf("http://tuf.%s.svc", instance.Namespace)
tufURL, err := utils.ResolveInternalServiceUrl(ctx, i.Client, instance.Spec.Api.Tuf, instance.Namespace, &rhtasv1.Tuf{})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there any reason for this change ?
Same as rekor url here, we prefer that we get TUF public url (if not, fallback to local url)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Console API is an in-cluster service, so using the internal service URL is simpler than routing through ingress. I switched it to use ResolveInternalServiceUrl with a new serviceresolver registration for TUF (same pattern as Trillian), which also handles user-configured URL/Ref overrides.

Are there plans for the Console dashboard to show the TUF URL to users? If so, I'll change it back to use the ingress endpoint.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are there plans for the Console dashboard to show the TUF URL to users?

Yes, this is what we do currently and this is why we prefer the route URL.
Example:
image

@osmman osmman Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thank you, I will use External url by default.

@osmman
osmman force-pushed the tturek/SECURESIGN-5066 branch 2 times, most recently from 5a26448 to 4229470 Compare August 5, 2026 13:35
Comment thread internal/controller/console/actions/api/deployment.go
Remove the deprecated RekorSearchUI component from the v1 API and
introduce a migration path that creates a Console CR for v1alpha1
users who had SearchUI enabled. Add cleanup action to remove
orphaned SearchUI resources (Deployment, Service, Ingress,
ServiceAccount, RoleBinding) and the UiAvailable status condition.

Introduce a shared internal/migration package providing annotation-
based helpers (Set, Read, Pop, Propagate, Remove) for preserving
v1alpha1-only fields through the conversion webhook.

Refs: SECURESIGN-5066

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tomas Turek <tturek@redhat.com>
@osmman
osmman force-pushed the tturek/SECURESIGN-5066 branch from 4229470 to d769fe5 Compare August 5, 2026 14:01
@osmman
osmman requested a review from fghanmi August 5, 2026 14:18
@osmman
osmman merged commit ff65bde into main Aug 5, 2026
20 of 23 checks passed
@osmman
osmman deleted the tturek/SECURESIGN-5066 branch August 5, 2026 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants