Skip to content

Conversation

@Leo6Leo
Copy link
Contributor

@Leo6Leo Leo6Leo commented Jan 5, 2026

Description

  • Add backend endpoint /api/olm/catalog-icons/{catalogName}/{packageName} to serve cached operator icons
  • Icons are fetched from catalogd on-demand and cached in memory with ETag/Last-Modified support
  • Frontend uses lazy loading (loading="lazy") for efficient icon loading in catalog tiles

How to test it

  1. Turn on the OLMv1 feature gate. Wait a moment until the reconciler finish working on your request.
oc patch featuregate/cluster --type=merge -p '{"spec":{"featureSet":"TechPreviewNoUpgrade"}}'
  1. Start port forwarding, so that the locally build console can access catalogd service.
   oc port-forward -n openshift-catalogd svc/catalogd-service 8080:443
  1. Set the env var to enable the bridge tech preview
export BRIDGE_TECH_PREVIEW=true
  1. Start your local bridge server
./examples/run-bridge.sh --k8s-mode-off-cluster-catalogd="https://localhost:8080"
  1. Go to User Preferences in the kebab menu on the top right corner, and make sure OLMv1 is enabled.
  2. Visit http://localhost:9000/catalog/ns/default?catalogType=operator
  3. You should see all the icons are correctly displaying. You can inspect the network requests to test that the cache is actually working.

Leo6Leo and others added 3 commits January 5, 2026 16:52
Adds infrastructure to cache operator icons from catalogd:
- CachedIcon struct to store icon data with ETag/LastModified headers
- FetchPackageIcon client method to query catalogd metas endpoint
- GetPackageIcon service method with in-memory caching
- parsePackageIcon to extract icon data from FBC package metadata

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds /api/olm/catalog-icons/{catalogName}/{packageName} endpoint that:
- Serves cached operator icons by package name
- Supports conditional requests with ETag and If-Modified-Since headers
- Returns 304 Not Modified when cache is still valid
- Sets appropriate Cache-Control headers for browser caching

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates frontend to use the new icon caching endpoint:
- Add icon URL pointing to /api/olm/catalog-icons/{catalog}/{package}
- Use lazy loading (loading="lazy") for efficient icon loading
- Return custom icon node from getIconProps for URL-based icons

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jan 5, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 5, 2026

@Leo6Leo: This pull request references CONSOLE-4728 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 story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Description

  • Add backend endpoint /api/olm/catalog-icons/{catalogName}/{packageName} to serve cached operator icons
  • Icons are fetched from catalogd on-demand and cached in memory with ETag/Last-Modified support
  • Frontend uses lazy loading (loading="lazy") for efficient icon loading in catalog tiles

How to test it

  1. Turn on the OLMv1 feature gate. Wait a moment until the reconciler finish working on your request.
oc patch featuregate/cluster --type=merge -p '{"spec":{"featureSet":"TechPreviewNoUpgrade"}}'
  1. Start port forwarding, so that the locally build console can access catalogd service.
  oc port-forward -n openshift-catalogd svc/catalogd-service 8080:443
  1. Set the env var to enable the bridge tech preview
export BRIDGE_TECH_PREVIEW=true
  1. Start your local bridge server
./examples/run-bridge.sh --k8s-mode-off-cluster-catalogd="https://localhost:8080"
  1. Go to User Preferences in the kebab menu on the top right corner, and make sure OLMv1 is enabled.
  2. Visit http://localhost:9000/catalog/ns/default?catalogType=operator
  3. You should see all the icons are correctly displaying. You can inspect the network requests to test that the cache is actually working.

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 openshift-ci bot requested review from TheRealJon and jhadvig January 5, 2026 21:59
@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

Walkthrough

Implements end-to-end icon caching and serving for operator packages. Frontend components render icons via a dynamic URL, backend routes requests through a new HTTP handler with ETag/If-Modified-Since support, fetches icons from catalogd on cache miss, and stores results locally.

Changes

Cohort / File(s) Summary
Frontend Icon Rendering
frontend/packages/console-shared/src/components/catalog/utils/catalog-utils.tsx, frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx
Modified icon handling to create lazy-loaded custom React nodes instead of plain URLs; replaced placeholder icon field with dynamic URL pattern /api/olm/catalog-icons/${catalog}/${name}
Backend Data Model
pkg/olm/catalog.go
Added new exported CachedIcon struct with Data []byte, MediaType, LastModified, and ETag fields
Backend Client Interface
pkg/olm/catalog_client.go
Extended CatalogdClientInterface with FetchPackageIcon method; implemented to build metas endpoint URL, append schema and package name query parameters, and perform GET request
Backend Service & Tests
pkg/olm/catalog_service.go, pkg/olm/catalog_service_test.go
Added GetPackageIcon method with cache-first lookup, catalogd fetch on miss, and icon parsing from metas JSON response; introduced internal parsePackageIcon helper; added mock client icon support and comprehensive test suite covering cache hits, fetches, not-found, and error cases
Backend Handler & Tests
pkg/olm/handler.go, pkg/olm/handler_test.go
Added HTTP route /api/olm/catalog-icons/{catalogName}/{packageName} with handler implementing ETag (If-None-Match) and If-Modified-Since conditional response logic; sets Cache-Control headers; introduced tests validating cached retrieval, 304 responses, cache misses, and 404 handling

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

✨ Finishing touches
  • 📝 Generate docstrings

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

@openshift-ci openshift-ci bot added the component/backend Related to backend label Jan 5, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 5, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Leo6Leo
Once this PR has been reviewed and has the lgtm label, please assign jhadvig for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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 component/olm Related to OLM component/shared Related to console-shared labels Jan 5, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Fix all issues with AI Agents 🤖
In
@frontend/packages/console-shared/src/components/catalog/utils/catalog-utils.tsx:
- Around line 268-271: The img element assigned to iconNode lacks an alt
attribute; update the creation of iconNode (the constant named iconNode) to
include a descriptive alt (e.g., alt={item?.name || "Operator icon"}) or alt=""
if the icon is purely decorative so that screen readers handle it correctly
while keeping loading="lazy" and the existing className.

In @frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx:
- Line 102: The URL is built by interpolating catalog and name directly into the
path, which can produce malformed or unsafe URLs; update the construction to
URL-encode both variables before insertion by applying encodeURIComponent to
catalog and name (so the icon.url uses the encoded catalog and encoded name) to
prevent special chars and path traversal issues and ensure the frontend matches
the router's decoded path handling.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 24830e3 and c5951b6.

📒 Files selected for processing (8)
  • frontend/packages/console-shared/src/components/catalog/utils/catalog-utils.tsx
  • frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx
  • pkg/olm/catalog.go
  • pkg/olm/catalog_client.go
  • pkg/olm/catalog_service.go
  • pkg/olm/catalog_service_test.go
  • pkg/olm/handler.go
  • pkg/olm/handler_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • pkg/olm/catalog_client.go
  • frontend/packages/console-shared/src/components/catalog/utils/catalog-utils.tsx
  • pkg/olm/handler.go
  • pkg/olm/catalog_service_test.go
  • pkg/olm/catalog_service.go
  • pkg/olm/handler_test.go
  • frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx
  • pkg/olm/catalog.go
🧬 Code graph analysis (5)
pkg/olm/handler.go (2)
pkg/middleware/middleware.go (1)
  • AllowMethod (86-88)
pkg/serverutils/utils.go (3)
  • SendResponse (13-28)
  • ApiError (87-89)
  • ModifiedSince (63-85)
pkg/olm/catalog_service_test.go (2)
pkg/olm/catalog.go (1)
  • CachedIcon (16-21)
pkg/olm/catalog_service.go (1)
  • CatalogService (35-41)
pkg/olm/catalog_service.go (1)
pkg/olm/catalog.go (1)
  • CachedIcon (16-21)
pkg/olm/handler_test.go (3)
pkg/olm/catalog.go (1)
  • CachedIcon (16-21)
pkg/olm/catalog_service.go (1)
  • NewCatalogService (59-66)
pkg/olm/handler.go (1)
  • NewOLMHandler (26-40)
frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx (1)
frontend/packages/integration-tests-cypress/views/catalogs.ts (1)
  • catalog (1-10)
🔇 Additional comments (13)
pkg/olm/catalog.go (1)

15-21: LGTM!

The CachedIcon struct is well-designed with appropriate fields for HTTP caching semantics (ETag, LastModified) and icon data (Data, MediaType).

pkg/olm/catalog_service_test.go (3)

20-27: LGTM!

The mock client extension with packageIconMap, fetchPackageErr, and fetchPackageCode provides good flexibility for testing various icon fetch scenarios.


34-70: LGTM!

The mock FetchPackageIcon implementation properly handles all scenarios: network errors, 404 responses, and successful icon retrieval with correctly formatted JSON output.


273-399: LGTM!

Comprehensive test coverage for GetPackageIcon including cache hit, cache miss with fetch, package not found, missing icon, and error propagation scenarios.

pkg/olm/handler.go (2)

35-35: LGTM!

Route registration follows the existing pattern and correctly uses middleware.AllowMethod for GET-only access.


99-153: LGTM!

The handler implementation is well-structured:

  • Proper input validation with clear error messages.
  • Correct ETag comparison using quoted format per HTTP spec.
  • Graceful handling of invalid If-Modified-Since headers.
  • Appropriate cache headers (24h max-age matching the service cache expiration).
pkg/olm/catalog_client.go (2)

16-16: LGTM!

Interface extension is consistent with existing methods.


94-118: LGTM!

The implementation follows the established pattern from other fetch methods, correctly builds the URL with query parameters, and logs the request appropriately.

pkg/olm/handler_test.go (2)

4-6: LGTM!

New imports are required for the test functionality (base64 encoding and formatting).


120-272: LGTM!

Excellent test coverage for the catalog icon handler covering:

  • Cache hit with proper headers
  • 404 scenarios (not found, URL pattern mismatch)
  • 304 responses for both ETag and If-Modified-Since
  • End-to-end fetch from catalogd on cache miss
pkg/olm/catalog_service.go (3)

55-57: LGTM!

Cache key helper follows the established naming pattern.


79-141: LGTM!

GetPackageIcon implements a robust cache-first pattern with:

  • Defensive handling of malformed cache entries
  • Proper response body cleanup
  • Clear status code handling (404 vs other errors)
  • Appropriate caching of fetched icons

143-177: LGTM!

The parsing implementation correctly uses WalkMetasReader for consistency with other catalog processing. The MD5-based ETag generation is appropriate for cache validation purposes.

Comment on lines +268 to +271
// Use a custom icon node with loading="lazy" for efficient loading
// eslint-disable-next-line jsx-a11y/alt-text
const iconNode = <img src={icon.url} loading="lazy" className="catalog-tile-pf-icon" />;
return { iconImg: null, iconClass: null, icon: iconNode };
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add alt text for accessibility compliance.

The image element lacks an alt attribute, which is essential for screen readers and accessibility. Even icon images should provide descriptive alternative text or an empty alt="" if purely decorative.

🔎 Proposed fix
-    // Use a custom icon node with loading="lazy" for efficient loading
-    // eslint-disable-next-line jsx-a11y/alt-text
-    const iconNode = <img src={icon.url} loading="lazy" className="catalog-tile-pf-icon" />;
+    // Use a custom icon node with loading="lazy" for efficient loading
+    const iconNode = <img src={icon.url} loading="lazy" className="catalog-tile-pf-icon" alt="" />;

Note: Use alt="" for decorative icons or provide a meaningful description if the icon conveys important information (e.g., alt={item.name} or alt="Operator icon").

📝 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
// Use a custom icon node with loading="lazy" for efficient loading
// eslint-disable-next-line jsx-a11y/alt-text
const iconNode = <img src={icon.url} loading="lazy" className="catalog-tile-pf-icon" />;
return { iconImg: null, iconClass: null, icon: iconNode };
// Use a custom icon node with loading="lazy" for efficient loading
const iconNode = <img src={icon.url} loading="lazy" className="catalog-tile-pf-icon" alt="" />;
return { iconImg: null, iconClass: null, icon: iconNode };
🤖 Prompt for AI Agents
In
@frontend/packages/console-shared/src/components/catalog/utils/catalog-utils.tsx
around lines 268-271, The img element assigned to iconNode lacks an alt
attribute; update the creation of iconNode (the constant named iconNode) to
include a descriptive alt (e.g., alt={item?.name || "Operator icon"}) or alt=""
if the icon is purely decorative so that screen readers handle it correctly
while keeping loading="lazy" and the existing className.

// Remove icon until we have an endpoint to lazy load cached icons.
// TODO Add icon back once https://issues.redhat.com/browse/CONSOLE-4728 is completed.
// icon: { url: '/api/olm/catalog-icons/<catalog-name>/<package-name> },
icon: { url: `/api/olm/catalog-icons/${catalog}/${name}` },
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, examine the file to understand the context around line 102
cat -n frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx | head -120 | tail -40

Repository: openshift/console

Length of output: 1674


🏁 Script executed:

# Check if there's any URL encoding or validation in the file
rg -n "encodeURI|decodeURI|sanitize|validate" frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Search for the backend endpoint handler that serves catalog-icons
rg -n -A 10 "catalog-icons" --type=go --type=ts --type=js

Repository: openshift/console

Length of output: 5915


🏁 Script executed:

# Look for any TypeScript type definitions for OLMCatalogItem to understand catalog/name types
rg -n "interface OLMCatalogItem|type OLMCatalogItem" --type=ts

Repository: openshift/console

Length of output: 368


🏁 Script executed:

# Find the catalogIconHandler implementation
rg -n -A 30 "func.*catalogIconHandler" pkg/olm/ --type=go

Repository: openshift/console

Length of output: 4178


🏁 Script executed:

# Search for any path validation or sanitization in the handler
rg -n "filepath|CleanPath|path\.Clean|Join" pkg/olm/ --type=go

Repository: openshift/console

Length of output: 129


🏁 Script executed:

# Check the full OLMCatalogItem type definition
cat -n frontend/packages/operator-lifecycle-manager-v1/src/types.ts | head -50

Repository: openshift/console

Length of output: 756


URL-encode path parameters to prevent malformed URLs and potential path traversal issues.

The catalog and name variables are interpolated directly into the URL path without encoding. If these values contain special characters (spaces, slashes, percent signs, etc.), the resulting URL will be malformed. Go's HTTP router automatically URL-decodes path segments, requiring the frontend to properly encode them before insertion.

Proposed fix
-    icon: { url: `/api/olm/catalog-icons/${catalog}/${name}` },
+    icon: { url: `/api/olm/catalog-icons/${encodeURIComponent(catalog)}/${encodeURIComponent(name)}` },
📝 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
icon: { url: `/api/olm/catalog-icons/${catalog}/${name}` },
icon: { url: `/api/olm/catalog-icons/${encodeURIComponent(catalog)}/${encodeURIComponent(name)}` },
🤖 Prompt for AI Agents
In @frontend/packages/operator-lifecycle-manager-v1/src/utils/catalog-item.tsx
around line 102, The URL is built by interpolating catalog and name directly
into the path, which can produce malformed or unsafe URLs; update the
construction to URL-encode both variables before insertion by applying
encodeURIComponent to catalog and name (so the icon.url uses the encoded catalog
and encoded name) to prevent special chars and path traversal issues and ensure
the frontend matches the router's decoded path handling.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 6, 2026

@Leo6Leo: The following tests 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/e2e-gcp-console c5951b6 link true /test e2e-gcp-console
ci/prow/okd-scos-images c5951b6 link true /test okd-scos-images

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/backend Related to backend component/olm Related to OLM component/shared Related to console-shared jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants