Skip to content

Add operator lifecycle metadata to installed operators table#16428

Draft
perdasilva wants to merge 1 commit into
openshift:mainfrom
perdasilva:upgrade-planner-2.0
Draft

Add operator lifecycle metadata to installed operators table#16428
perdasilva wants to merge 1 commit into
openshift:mainfrom
perdasilva:upgrade-planner-2.0

Conversation

@perdasilva
Copy link
Copy Markdown

@perdasilva perdasilva commented May 11, 2026

Analysis / Root cause:
The console needs to surface operator lifecycle metadata (support status, OpenShift version compatibility) directly in the installed operators table. Previously, this data was fetched from a separate lifecycle-server HTTP sidecar. This PR switches to querying the CatalogSource's native gRPC ListPackageCustomSchemas endpoint, eliminating the need for a dedicated sidecar service.

Solution description:

Backend:

  • New lifecycle handler in pkg/olm/lifecycle.go dials the CatalogSource gRPC Registry service at {catalogName}.{namespace}.svc:50051
  • Calls ListPackageCustomSchemas with schema io.openshift.operators.lifecycles.v1alpha1 and the package name
  • Reads the streamed google.protobuf.Struct response and returns it as JSON
  • Handles older CatalogSources that don't implement ListPackageCustomSchemas by returning HTTP 503 (maps from gRPC codes.Unimplemented), so the frontend gracefully shows "no data"
  • Handles codes.Unavailable → HTTP 503, empty stream → HTTP 404

Frontend:

  • New useOperatorLifecycle hook fetches lifecycle data from the backend proxy with client-side caching (5 min success, 30s error TTL)
  • New operator-lifecycle-status component renders support phase labels and OpenShift compatibility status
  • Installed operators table gains "Support Status" and "OpenShift Compatibility" columns
  • Feature-gated behind a tech preview flag

Screenshots / screen recording:

Test setup:

  • Deploy a CatalogSource running an opm version that supports ListPackageCustomSchemas with lifecycle schema data loaded
  • Install an operator from that CatalogSource
  • Verify lifecycle columns appear in the installed operators table

Test cases:

  • Operator from catalog with lifecycle data → shows support status and compatibility
  • Operator from catalog without lifecycle data → shows "no data" gracefully
  • Operator from catalog running older opm (no ListPackageCustomSchemas) → shows "no data" (503 handled)
  • Invalid catalog namespace/name → returns 400
  • Non-existent package → returns 404

Browser conformance:

  • Chrome
  • Firefox
  • Safari (or Epiphany on Linux)

Additional info:

  • The go.mod includes a replace directive for operator-framework/operator-registry pointing to a local fork that adds the ListPackageCustomSchemas endpoint. This will need to be updated to the published version once released.
  • Vendor directory changes are from the operator-registry dependency update.

🤖 Generated with Claude Code

@perdasilva perdasilva marked this pull request as draft May 11, 2026 12:50
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 11, 2026
@openshift-ci openshift-ci Bot requested review from Leo6Leo and jhadvig May 11, 2026 12:51
@openshift-ci openshift-ci Bot added the component/backend Related to backend label May 11, 2026
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 11, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: perdasilva
Once this PR has been reviewed and has the lgtm label, please assign therealjon 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 kind/i18n Indicates issue or PR relates to internationalization or has content that needs to be translated labels May 11, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

This pull request adds operator lifecycle metadata support to OpenShift Console. It introduces a new gRPC-based backend endpoint that fetches lifecycle data from Operator Registry, adds a tech-preview feature flag to gate the functionality, implements a React hook with in-memory caching and request deduplication to consume the endpoint, and integrates new "Cluster Compatibility" and "Support" columns into the Installed Operators table. The columns render compatibility status (compatible/incompatible/no-data) and support phase information with formatted tooltips. RBAC is restricted via ClusterRole/ClusterRoleBinding. UI text is localized and column visibility is controlled via user preferences.

🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (11 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and specifically summarizes the primary change: adding operator lifecycle metadata to the installed operators table.
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 All test names in the PR are stable and static. Go tests use t.Run() with static names; React tests use jest it()/describe() with static descriptions. No Ginkgo patterns or dynamic test names found.
Test Structure And Quality ✅ Passed Check not applicable: PR adds no Ginkgo tests. Go tests use standard testing.T with table-driven patterns and meaningful assertions; frontend tests use Jest/RTL. Both follow repository conventions.
Microshift Test Compatibility ✅ Passed No Ginkgo e2e tests added. Two test files present: Jest/RTL unit test for React components and Go unit test using standard testing package. Check does not apply.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests were added. PR contains Go unit tests and Jest/RTL tests, neither of which are Ginkgo. Custom check not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed No scheduling constraints, topology assumptions, Deployments, affinity rules, nodeSelectors, or PDB definitions added. Changes are RBAC, frontend code, and a stateless HTTP handler.
Ote Binary Stdout Contract ✅ Passed PR respects OTE Binary Stdout Contract: klog writes to stderr by default, all HTTP responses are JSON-serialized, no process-level stdout writes, and tests don't contaminate stdout.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests found in this PR. Added tests are standard Go unit tests (testing package) and Jest/React component tests, not Ginkgo. Check is not applicable.
Description check ✅ Passed PR description covers analysis, backend/frontend solution, test setup, test cases, and additional context about the operator-registry dependency.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (10)
contrib/lifecycle-rbac.yaml (1)

1-6: 💤 Low value

Documentation comment may be outdated.

The comment references "lifecycle-server API" and authentication via TokenReview/SubjectAccessReview, but the implementation in pkg/olm/lifecycle.go directly dials the CatalogSource gRPC service without a separate lifecycle-server. Consider updating to reflect the actual architecture:

# RBAC required for the OpenShift Console to access the lifecycle metadata API.
#
# The console backend proxies lifecycle metadata requests by dialing
# CatalogSource gRPC services directly. This RBAC grants the console
# ServiceAccount permission to access the /api/olm/lifecycle/* endpoints.
🤖 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 `@contrib/lifecycle-rbac.yaml` around lines 1 - 6, Update the top comment in
contrib/lifecycle-rbac.yaml to reflect current architecture: replace the
outdated "lifecycle-server API" and TokenReview/SubjectAccessReview description
with wording that the console backend proxies lifecycle metadata by dialing
CatalogSource gRPC services directly and that this RBAC grants the console
ServiceAccount permission to access the /api/olm/lifecycle/* endpoints (refer to
implementation in pkg/olm/lifecycle.go for the actual flow).
pkg/olm/lifecycle_test.go (3)

96-131: 💤 Low value

Consider adding test case for non-gRPC errors.

handleGRPCError has a fallback path when grpcstatus.FromError returns ok=false (e.g., for plain Go errors). Adding a test case for this branch improves coverage:

{
    name:           "non-gRPC error returns 502",
    err:            fmt.Errorf("plain error"),
    expectedStatus: http.StatusBadGateway,
},
🤖 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 `@pkg/olm/lifecycle_test.go` around lines 96 - 131, Add a test case to
TestHandleGRPCError that covers the non-gRPC fallback by invoking
handleGRPCError with a plain Go error (e.g., fmt.Errorf("plain error")) and
asserting it returns http.StatusBadGateway; update the imports in
lifecycle_test.go to include "fmt" if not present and add the test case entry
(name "non-gRPC error returns 502", err fmt.Errorf(...), expectedStatus
http.StatusBadGateway) to the tests slice so the branch where
grpcstatus.FromError returns ok=false is exercised.

69-94: 💤 Low value

Consider adding test for missing packageName validation.

The validation tests cover invalid namespace and catalog names but not the empty packageName case. Since lifecycleHandler explicitly validates this (returning 400), it's worth testing:

t.Run("rejects empty packageName", func(t *testing.T) {
    // Note: This requires a route that allows empty packageName to reach handler
    // or testing the handler directly with a mock request
    req := httptest.NewRequest("GET", "/api/olm/lifecycle/openshift-marketplace/redhat-operators/", nil)
    rr := httptest.NewRecorder()
    mux.ServeHTTP(rr, req)
    assert.Equal(t, http.StatusBadRequest, rr.Code)
})
🤖 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 `@pkg/olm/lifecycle_test.go` around lines 69 - 94, Add a test case to
TestOLMHandler_lifecycleHandler_validation that asserts lifecycleHandler rejects
an empty packageName: create a subtest "rejects empty packageName" that sends a
GET request with an empty final segment (e.g.
"/api/olm/lifecycle/openshift-marketplace/redhat-operators/") and verifies
http.StatusBadRequest; if the ServeMux route prevents matching an empty segment,
call handler.lifecycleHandler directly with an httptest.NewRequest whose
URL.Path ends without a package name and a ResponseRecorder, or construct the
request so the handler receives an empty packageName value, then assert rr.Code
== http.StatusBadRequest.

42-67: 💤 Low value

Good edge case coverage. Consider adding a few more cases.

The validation tests cover important security cases (path traversal, domain injection). Consider adding:

  1. A test for exactly 63 characters (max valid length per DNS-label spec)
  2. A test for numeric-only names like "123" which should be valid
{"a23456789012345678901234567890123456789012345678901234567890123", true}, // 63 chars - max valid
{"123", true}, // numeric-only is valid per DNS label
🤖 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 `@pkg/olm/lifecycle_test.go` around lines 42 - 67, Add two more table-driven
cases to TestIsValidK8sName: a 63-character string and a numeric-only name
"123". Update the tests slice in TestIsValidK8sName to include
{"a23456789012345678901234567890123456789012345678901234567890123", true} and
{"123", true}, then run the subtests that call isValidK8sName to verify both are
treated as valid by the validator.
pkg/olm/lifecycle.go (4)

98-101: 💤 Low value

Handle write error for observability.

The error from w.Write is discarded. While the response is already committed, logging the error aids debugging network issues:

 	w.Header().Set("Content-Type", "application/json")
 	w.WriteHeader(http.StatusOK)
-	w.Write(jsonBytes)
+	if _, err := w.Write(jsonBytes); err != nil {
+		klog.Errorf("[lifecycle] Failed to write response: %v", err)
+	}
 }
🤖 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 `@pkg/olm/lifecycle.go` around lines 98 - 101, The call to w.Write(jsonBytes)
currently discards its returned error which hurts observability; update the
handler (the block that sets Content-Type and writes jsonBytes to the
http.ResponseWriter) to capture the error from w.Write(jsonBytes) and log it
(include context such as the endpoint/operation and the jsonBytes length) using
the package's logger (or log.Printf/klog) so network/write failures are
observable; keep the existing w.WriteHeader(http.StatusOK) behavior but ensure
the write error is checked and logged.

38-38: 💤 Low value

Consider using debug-level logging for routine request tracing.

Per coding guidelines, use klog.V(4) for debug logging. Logging every incoming request at Info level can be noisy in production. Reserve Info for significant events.

-	klog.Infof("[lifecycle] Received request: catalogNamespace=%q catalogName=%q packageName=%q", catalogNamespace, catalogName, packageName)
+	klog.V(4).Infof("[lifecycle] Received request: catalogNamespace=%q catalogName=%q packageName=%q", catalogNamespace, catalogName, packageName)

Same applies to lines 59 and 97 for dial target logging and successful responses.

🤖 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 `@pkg/olm/lifecycle.go` at line 38, Change the three routine request/response
Info logs to debug-level per guidelines: replace the klog.Infof calls that log
incoming request (the call with catalogNamespace, catalogName, packageName), the
dial-target logging call (the log that prints the dial target), and the
successful response log with klog.V(4).Infof so they become verbose-level debug
messages; keep the same message text and formatting but use klog.V(4).Infof to
reduce noise in production.

52-56: ⚡ Quick win

Consider validating packageName format for consistency.

catalogNamespace and catalogName are validated against the DNS-label regex, but packageName only checks for empty. OLM package names typically follow similar naming conventions. Consider applying the same validation for defense in depth:

 	if packageName == "" {
 		klog.Infof("[lifecycle] Missing packageName")
 		serverutils.SendResponse(w, http.StatusBadRequest, serverutils.ApiError{Err: "packageName is required"})
 		return
 	}
+
+	if !isValidK8sName(packageName) {
+		klog.Infof("[lifecycle] Invalid packageName: %q", packageName)
+		serverutils.SendResponse(w, http.StatusBadRequest, serverutils.ApiError{Err: "invalid packageName"})
+		return
+	}
🤖 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 `@pkg/olm/lifecycle.go` around lines 52 - 56, packageName is only checked for
emptiness while catalogNamespace and catalogName use DNS-label validation; apply
the same DNS-label check to packageName by reusing the existing validation logic
used for catalogNamespace/catalogName (the same regex or helper function) and
return the same http.StatusBadRequest via serverutils.SendResponse with a clear
error like "packageName must be a valid DNS-1123 label"; also log the failure
with klog.Infof similar to the other validations.

61-67: ⚡ Quick win

Consider adding dial options for resilience.

The gRPC client is created without explicit timeout constraints. While grpc.NewClient is non-blocking by default and stream.Recv() respects the request context, consider adding dial options for defensive programming:

-	conn, err := grpc.NewClient(target, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	conn, err := grpc.NewClient(target,
+		grpc.WithTransportCredentials(insecure.NewCredentials()),
+		grpc.WithDefaultCallOptions(grpc.WaitForReady(false)),
+	)

This ensures the client fails fast if the service is unavailable rather than potentially waiting for connection establishment.

🤖 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 `@pkg/olm/lifecycle.go` around lines 61 - 67, The gRPC client dial currently
calls grpc.NewClient(target,
grpc.WithTransportCredentials(insecure.NewCredentials())) without
blocking/timeout/backoff options; update the call that creates the connection
(the grpc.NewClient invocation that returns conn, err) to use dial options that
enforce a finite dial timeout and clearer failure semantics (e.g., use a context
with timeout for the dial, add grpc.WithBlock() and
grpc.WithReturnConnectionError(), and configure connect params/backoff via
grpc.WithConnectParams) so the dial fails fast when the service is unreachable
and returns a meaningful error to the existing klog.Errorf and
serverutils.SendResponse paths.
frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx (1)

372-402: ⚡ Quick win

Drop the lifecycle console.logs before merge.

These execute on every installed-operator row render and dump large objects like lifecycleData, so a busy table will spam production consoles and add avoidable work in an already hot render path.

Also applies to: 585-595

🤖 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
`@frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx`
around lines 372 - 402, Remove the debug console.log statements that log
lifecycle params/results from clusterserviceversion.tsx to avoid spamming
renders; specifically delete the two console.log blocks surrounding the
useOperatorLifecycle hook and lifecycle result (the blocks that reference
'[lifecycle-csv-row] Lifecycle params:' and '[lifecycle-csv-row] Lifecycle
result:' and print lifecycleData, clusterVersion, compatible, supportPhase,
etc.). Ensure you do not change logic that calls useOperatorLifecycle,
getClusterVersion, getClusterCompatibility, or getSupportPhase—only remove the
console.log lines (also remove the duplicate logs around lines 585-595 noted in
the comment).
frontend/packages/operator-lifecycle-manager/src/hooks/useOperatorLifecycle.ts (1)

93-148: ⚡ Quick win

Please remove the fetch-debug logging from this hook.

Once the installed-operators table mounts several lifecycle hooks, these logs will get very noisy very quickly. If you still need them while iterating, please gate them behind a dev-only logger instead of shipping raw console.log/console.error.

🤖 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
`@frontend/packages/operator-lifecycle-manager/src/hooks/useOperatorLifecycle.ts`
around lines 93 - 148, Remove the ad-hoc console logging in the
useOperatorLifecycle hook: eliminate all console.log and console.error calls
inside the useOperatorLifecycle function (including the initial call logging
packageName/catalogName/catalogNamespace, the "Skipping fetch" branch, the
"Fetching", "Fetch success" and "Fetch error" logs inside the useEffect, and any
related debug prints around fetchLifecycleData/abortRef). If you still need
runtime debug output during local development, replace those calls with a
dev-only logger wrapper (checked by NODE_ENV or a dev flag) instead of shipping
raw console calls, keeping the rest of the logic (buildCacheKey, lifecycleCache
lookup, fetchLifecycleData handling, and abortRef usage) unchanged.
🤖 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 `@contrib/lifecycle-rbac.yaml`:
- Around line 17-18: The nonResourceURLs entry currently uses an overly broad
pattern "/api/*/lifecycles/*"; update the nonResourceURLs value to the specific
route used by the service (e.g. "/api/olm/lifecycle/*") so the RBAC rule only
matches the intended endpoint—modify the nonResourceURLs array item (the string
literal) to the tightened pattern to follow least privilege.

In
`@frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx`:
- Around line 25-29: The file currently imports ColumnLayout, WatchK8sResource,
and WatchK8sResultsObject from the package index '@console/dynamic-plugin-sdk';
change that to import each type from its concrete module path within the
dynamic-plugin-sdk (i.e., replace the single-barrel import at the top of
clusterserviceversion.tsx with individual imports that reference the specific
module files that export ColumnLayout, WatchK8sResource, and
WatchK8sResultsObject), ensuring you only update the import statement(s) and
keep the symbol names (ColumnLayout, WatchK8sResource, WatchK8sResultsObject)
unchanged where they are used.

In
`@frontend/packages/operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx`:
- Around line 97-106: The phase date strings (phase.timeBegin / phase.timeEnd)
are being parsed with new Date('YYYY-MM-DD') which yields UTC midnight and
causes timezone-dependent boundary errors; instead parse the YYYY-MM-DD strings
into local calendar-day boundaries and compare against now: for each phase in
allPhases convert timeBegin into a local start-of-day (new Date(year, month-1,
day) or equivalent) and convert timeEnd into the local end-of-day (set to
23:59:59.999) before computing begin/end, then use now >= begin && now <= end;
update the same parsing logic used around the other block (lines referenced
141-147) so all comparisons treat dates as local calendar dates rather than UTC
instants.

In `@go.mod`:
- Line 242: Remove the workstation-local replace entry for the module
"github.com/operator-framework/operator-registry" (which currently points to an
absolute path) and replace it with a reachable module reference such as an
official tagged version, a git tag/ref, or a pseudo-version (avoid absolute
filesystem paths); also verify the intentionality of the Docker module pins (the
pinned "v27.5.0+incompatible" entries) and either keep them with a comment
explaining why they must be downgraded or update them to the desired versions
consistent with transitive dependency resolution.

In `@pkg/olm/lifecycle.go`:
- Line 9: The go.mod replace directive pointing to a local filesystem path must
be removed or replaced with a persistent public fork URL so CI and other devs
can build; locate the replace entry in go.mod and either delete it or change it
to a public Git remote (e.g., github.com/<your-fork>/operator-registry vX.Y.Z)
and run `go mod tidy`; also reconcile use of the fork-only API GetCustomSchemas
in lifecycle.go by either removing or replacing calls to GetCustomSchemas (or
wrapping them behind a compat shim) if you intend to track upstream, or ensure
the go.mod replace references your public fork that actually implements
GetCustomSchemas so the symbol resolves.

---

Nitpick comments:
In `@contrib/lifecycle-rbac.yaml`:
- Around line 1-6: Update the top comment in contrib/lifecycle-rbac.yaml to
reflect current architecture: replace the outdated "lifecycle-server API" and
TokenReview/SubjectAccessReview description with wording that the console
backend proxies lifecycle metadata by dialing CatalogSource gRPC services
directly and that this RBAC grants the console ServiceAccount permission to
access the /api/olm/lifecycle/* endpoints (refer to implementation in
pkg/olm/lifecycle.go for the actual flow).

In
`@frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx`:
- Around line 372-402: Remove the debug console.log statements that log
lifecycle params/results from clusterserviceversion.tsx to avoid spamming
renders; specifically delete the two console.log blocks surrounding the
useOperatorLifecycle hook and lifecycle result (the blocks that reference
'[lifecycle-csv-row] Lifecycle params:' and '[lifecycle-csv-row] Lifecycle
result:' and print lifecycleData, clusterVersion, compatible, supportPhase,
etc.). Ensure you do not change logic that calls useOperatorLifecycle,
getClusterVersion, getClusterCompatibility, or getSupportPhase—only remove the
console.log lines (also remove the duplicate logs around lines 585-595 noted in
the comment).

In
`@frontend/packages/operator-lifecycle-manager/src/hooks/useOperatorLifecycle.ts`:
- Around line 93-148: Remove the ad-hoc console logging in the
useOperatorLifecycle hook: eliminate all console.log and console.error calls
inside the useOperatorLifecycle function (including the initial call logging
packageName/catalogName/catalogNamespace, the "Skipping fetch" branch, the
"Fetching", "Fetch success" and "Fetch error" logs inside the useEffect, and any
related debug prints around fetchLifecycleData/abortRef). If you still need
runtime debug output during local development, replace those calls with a
dev-only logger wrapper (checked by NODE_ENV or a dev flag) instead of shipping
raw console calls, keeping the rest of the logic (buildCacheKey, lifecycleCache
lookup, fetchLifecycleData handling, and abortRef usage) unchanged.

In `@pkg/olm/lifecycle_test.go`:
- Around line 96-131: Add a test case to TestHandleGRPCError that covers the
non-gRPC fallback by invoking handleGRPCError with a plain Go error (e.g.,
fmt.Errorf("plain error")) and asserting it returns http.StatusBadGateway;
update the imports in lifecycle_test.go to include "fmt" if not present and add
the test case entry (name "non-gRPC error returns 502", err fmt.Errorf(...),
expectedStatus http.StatusBadGateway) to the tests slice so the branch where
grpcstatus.FromError returns ok=false is exercised.
- Around line 69-94: Add a test case to
TestOLMHandler_lifecycleHandler_validation that asserts lifecycleHandler rejects
an empty packageName: create a subtest "rejects empty packageName" that sends a
GET request with an empty final segment (e.g.
"/api/olm/lifecycle/openshift-marketplace/redhat-operators/") and verifies
http.StatusBadRequest; if the ServeMux route prevents matching an empty segment,
call handler.lifecycleHandler directly with an httptest.NewRequest whose
URL.Path ends without a package name and a ResponseRecorder, or construct the
request so the handler receives an empty packageName value, then assert rr.Code
== http.StatusBadRequest.
- Around line 42-67: Add two more table-driven cases to TestIsValidK8sName: a
63-character string and a numeric-only name "123". Update the tests slice in
TestIsValidK8sName to include
{"a23456789012345678901234567890123456789012345678901234567890123", true} and
{"123", true}, then run the subtests that call isValidK8sName to verify both are
treated as valid by the validator.

In `@pkg/olm/lifecycle.go`:
- Around line 98-101: The call to w.Write(jsonBytes) currently discards its
returned error which hurts observability; update the handler (the block that
sets Content-Type and writes jsonBytes to the http.ResponseWriter) to capture
the error from w.Write(jsonBytes) and log it (include context such as the
endpoint/operation and the jsonBytes length) using the package's logger (or
log.Printf/klog) so network/write failures are observable; keep the existing
w.WriteHeader(http.StatusOK) behavior but ensure the write error is checked and
logged.
- Line 38: Change the three routine request/response Info logs to debug-level
per guidelines: replace the klog.Infof calls that log incoming request (the call
with catalogNamespace, catalogName, packageName), the dial-target logging call
(the log that prints the dial target), and the successful response log with
klog.V(4).Infof so they become verbose-level debug messages; keep the same
message text and formatting but use klog.V(4).Infof to reduce noise in
production.
- Around line 52-56: packageName is only checked for emptiness while
catalogNamespace and catalogName use DNS-label validation; apply the same
DNS-label check to packageName by reusing the existing validation logic used for
catalogNamespace/catalogName (the same regex or helper function) and return the
same http.StatusBadRequest via serverutils.SendResponse with a clear error like
"packageName must be a valid DNS-1123 label"; also log the failure with
klog.Infof similar to the other validations.
- Around line 61-67: The gRPC client dial currently calls grpc.NewClient(target,
grpc.WithTransportCredentials(insecure.NewCredentials())) without
blocking/timeout/backoff options; update the call that creates the connection
(the grpc.NewClient invocation that returns conn, err) to use dial options that
enforce a finite dial timeout and clearer failure semantics (e.g., use a context
with timeout for the dial, add grpc.WithBlock() and
grpc.WithReturnConnectionError(), and configure connect params/backoff via
grpc.WithConnectParams) so the dial fails fast when the service is unreachable
and returns a meaningful error to the existing klog.Errorf and
serverutils.SendResponse paths.
🪄 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 YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: dfea84d5-aae7-4cc6-b70b-037dac55f937

📥 Commits

Reviewing files that changed from the base of the PR and between 1687e65 and 555f8d7.

⛔ Files ignored due to path filters (286)
  • go.sum is excluded by !**/*.sum
  • vendor/cel.dev/expr/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/MODULE.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/checked.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/eval.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/explain.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/syntax.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/value.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/dario.cat/mergo/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/dario.cat/mergo/SECURITY.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/AdaLogics/go-fuzz-headers/consumer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/antlrdoc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/atn.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/atn_config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/input_stream.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/jcollect.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/lexer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/ll1_analyzer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/parser_atn_simulator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/prediction_context.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/recognizer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/statistics.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/token.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/antlr4-go/antlr/v4/utils.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/ecc/goldilocks/curve.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/internal/conv/conv.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/sign/ed25519/point.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/sign/ed448/ed448.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/cloudflare/circl/sign/sign.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/containerd/containerd/version/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/docker/docker-credential-helpers/client/command.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/docker/go-connections/tlsconfig/config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/docker/go-connections/tlsconfig/config_client_ciphers.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/emicklei/go-restful/v3/.travis.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/emicklei/go-restful/v3/CHANGES.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/emicklei/go-restful/v3/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/emicklei/go-restful/v3/curly.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/emicklei/go-restful/v3/custom_verb.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/emicklei/go-restful/v3/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-billy/v5/fs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-billy/v5/helper/chroot/chroot.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-billy/v5/memfs/memory.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-billy/v5/memfs/storage.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-billy/v5/osfs/os_chroot.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/options.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/plumbing/transport/http/transport.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/auth_method.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/remote.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/repository.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/utils/merkletrie/index/node.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/worktree.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-git/go-git/v5/worktree_status.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/pointer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/reference.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/BENCHMARK.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/convert.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/convert_types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/file.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/initialism_index.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/json.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/name_lexem.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/net.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/path.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/split.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/string_bytes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/util.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yaml.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/golang/mock/gomock/call.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/golang/mock/gomock/controller.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/folding.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/library.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/optimizer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/program.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/templates/authoring.tmpl is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/validator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/checker/checker.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/checker/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/checker/scopes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/ast/ast.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/debug/debug.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/env/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/env/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/bool.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/bytes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/double.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/duration.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/int.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/json_value.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/list.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/map.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/null.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/object.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/pb/type.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/string.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/timestamp.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/uint.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/attribute_patterns.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/attributes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/interpretable.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/interpreter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/planner.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/parser/helper.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/extensions/extension.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/openapiv3/annotations.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/hashicorp/go-retryablehttp/.go-version is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/hashicorp/go-retryablehttp/CODEOWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/hashicorp/go-retryablehttp/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/hashicorp/go-retryablehttp/client.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/josharian/intern/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/josharian/intern/intern.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/josharian/intern/license.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/fse/bitwriter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/fse/compress.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/huff0/bitwriter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/huff0/compress.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/huff0/decompress.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/huff0/decompress_amd64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/huff0/huff0.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/internal/le/unsafe_disabled.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/internal/le/unsafe_enabled.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/internal/snapref/decode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/internal/snapref/encode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/bitwriter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/blockdec.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/decoder.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/decoder_options.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/dict.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/enc_base.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/enc_best.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/enc_better.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/enc_dfast.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/enc_fast.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/encoder.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/encoder_options.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/framedec.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/fse_encoder.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/seqdec.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/snappy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/zip.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/klauspost/compress/zstd/zstd.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/buffer/pool.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/bytestostr.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/error.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/lexer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jwriter/writer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mattn/go-runewidth/runewidth.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mattn/go-runewidth/runewidth_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/catalogsource_types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/clusterserviceversion_types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/operator-framework/operator-registry/alpha/declcfg/declcfg.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/alpha/declcfg/declcfg_to_model.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/alpha/declcfg/load.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/alpha/declcfg/model_to_declcfg.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/alpha/declcfg/write.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/alpha/model/error.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/alpha/model/model.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/alpha/property/property.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/api/api_to_model.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/api/model_to_api.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/api/registry.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/api/registry.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/api/registry_grpc.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/image/mock.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/lib/semver/semver.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/prettyunmarshaler/prettyunmarshaler.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/bundle.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/bundlegraphloader.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/channelupdateoptions.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/conversion.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/csv.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/decode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/directoryGraphLoader.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/empty.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/graph.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/imageinput.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/parse.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/populator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/registry_to_model.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/operator-framework/operator-registry/pkg/registry/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/Dockerfile.arm is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/sha1cd.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/sha1cdblock_amd64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/ubc/check.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/pjbgf/sha1cd/ubc/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/decode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/expfmt.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/fuzz.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/openmetrics_create.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/text_create.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/text_parse.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/Makefile.common is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/arp.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/buddyinfo.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cmdline.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_armx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_loong64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_others.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_s390x.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_x86.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/crypto.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/fs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/fs_statfs_notype.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/fs_statfs_type.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/fscache.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/fs/fs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/util/parse.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/util/readfile.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/util/valueparser.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/ipvs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/kernel_random.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/loadavg.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/mdstat.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/meminfo.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/mountinfo.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/mountstats.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_conntrackstat.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_dev.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_dev_snmp6.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_ip_socket.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_protocols.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_route.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_sockstat.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_softnet.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_tcp.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_tls_stat.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_udp.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_unix.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_wireless.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_xfrm.go is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (14)
  • contrib/lifecycle-rbac.yaml
  • frontend/packages/operator-lifecycle-manager/console-extensions.json
  • frontend/packages/operator-lifecycle-manager/locales/en/olm.json
  • frontend/packages/operator-lifecycle-manager/package.json
  • frontend/packages/operator-lifecycle-manager/src/components/__tests__/operator-lifecycle-status.spec.tsx
  • frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx
  • frontend/packages/operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx
  • frontend/packages/operator-lifecycle-manager/src/const.ts
  • frontend/packages/operator-lifecycle-manager/src/features.ts
  • frontend/packages/operator-lifecycle-manager/src/hooks/useOperatorLifecycle.ts
  • go.mod
  • pkg/olm/handler.go
  • pkg/olm/lifecycle.go
  • pkg/olm/lifecycle_test.go

Comment on lines +17 to +18
- nonResourceURLs:
- "/api/*/lifecycles/*"
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

The nonResourceURLs pattern is broader than necessary.

The pattern "/api/*/lifecycles/*" would match any API path containing /lifecycles/, but the actual endpoint is /api/olm/lifecycle/.... Consider tightening to match the specific route:

   - nonResourceURLs:
-      - "/api/*/lifecycles/*"
+      - "/api/olm/lifecycle/*"
     verbs:
       - "get"

This follows the principle of least privilege and prevents unintended access to future endpoints that might match the broader pattern.

🤖 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 `@contrib/lifecycle-rbac.yaml` around lines 17 - 18, The nonResourceURLs entry
currently uses an overly broad pattern "/api/*/lifecycles/*"; update the
nonResourceURLs value to the specific route used by the service (e.g.
"/api/olm/lifecycle/*") so the RBAC rule only matches the intended
endpoint—modify the nonResourceURLs array item (the string literal) to the
tightened pattern to follow least privilege.

Comment on lines +25 to +29
import type {
ColumnLayout,
WatchK8sResource,
WatchK8sResultsObject,
} from '@console/dynamic-plugin-sdk';
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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Avoid adding new barrel imports from @console/dynamic-plugin-sdk.

These new type imports come from the package index, which this repo explicitly avoids in new TS/TSX code because it obscures dependency edges and has caused circular-build issues before. Please import them from their concrete module paths instead.

As per coding guidelines, "Never import from package index files (e.g., @console/shared) in new code; import from specific file paths instead to avoid circular dependencies and slow builds".

🤖 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
`@frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx`
around lines 25 - 29, The file currently imports ColumnLayout, WatchK8sResource,
and WatchK8sResultsObject from the package index '@console/dynamic-plugin-sdk';
change that to import each type from its concrete module path within the
dynamic-plugin-sdk (i.e., replace the single-barrel import at the top of
clusterserviceversion.tsx with individual imports that reference the specific
module files that export ColumnLayout, WatchK8sResource, and
WatchK8sResultsObject), ensuring you only update the import statement(s) and
keep the symbol names (ColumnLayout, WatchK8sResource, WatchK8sResultsObject)
unchanged where they are used.

Comment on lines +97 to +106
for (const phase of allPhases) {
const begin = new Date(phase.timeBegin).getTime();
const end = new Date(phase.timeEnd).getTime();
if (now >= begin && now <= end) {
return { currentPhase: phase, allPhases };
}
}

const lastPhase = allPhases[allPhases.length - 1];
if (now > new Date(lastPhase.timeEnd).getTime()) {
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate the specific file
find . -path "*operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx" -type f

Repository: openshift/console

Length of output: 153


🏁 Script executed:

#!/bin/bash
# Read the file to examine lines 97-106 and 141-147
FILE=$(find . -path "*operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx" -type f)
if [ -f "$FILE" ]; then
  echo "=== Full file context ==="
  wc -l "$FILE"
  echo "=== Lines 80-160 (surrounding context) ==="
  sed -n '80,160p' "$FILE"
fi

Repository: openshift/console

Length of output: 2468


🏁 Script executed:

#!/bin/bash
# Check for PatternFly Label usage patterns in the file
FILE=$(find . -path "*operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx" -type f)
if [ -f "$FILE" ]; then
  echo "=== PatternFly imports and Label usage ==="
  grep -n "Label\|import.*patternfly\|from '@patternfly" "$FILE" | head -20
fi

Repository: openshift/console

Length of output: 690


🌐 Web query:

PatternFly Label component React API status prop color

💡 Result:

In PatternFly React, the Label component has a status prop and a color prop. The status prop controls the status icon and color, and it overrides the color prop. - status prop: valid values include 'success', 'warning', 'danger', 'info', and 'custom'. It “overrides the color set by the color property.” [1] - color prop: “Color of the label.” [1] - icon prop: “Overrides the icon set by the status property.” [1] So, if you pass both status and color, status wins for both icon+color (color won’t override status). If you also pass icon, icon wins over the status icon. [1] Example implication: will render using the success status styling, not red. [1]

Citations:


YYYY-MM-DD parsing makes support dates timezone-dependent.

new Date('2025-12-31') is parsed as UTC midnight, but toLocaleDateString() formats it in local time. Users west of UTC will see support windows shifted—for example, PST users will see a support end date one calendar day earlier than intended. The comparison logic now >= begin && now <= end evaluates the same boundary differently across timezones. Treat these as calendar dates in the user's local timezone, not UTC instants.

Also applies to: 141-147

🤖 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
`@frontend/packages/operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx`
around lines 97 - 106, The phase date strings (phase.timeBegin / phase.timeEnd)
are being parsed with new Date('YYYY-MM-DD') which yields UTC midnight and
causes timezone-dependent boundary errors; instead parse the YYYY-MM-DD strings
into local calendar-day boundaries and compare against now: for each phase in
allPhases convert timeBegin into a local start-of-day (new Date(year, month-1,
day) or equivalent) and convert timeEnd into the local end-of-day (set to
23:59:59.999) before computing begin/end, then use now >= begin && now <= end;
update the same parsing logic used around the other block (lines referenced
141-147) so all comparisons treat dates as local calendar dates rather than UTC
instants.

Comment on lines +44 to +80
const fetchLifecycleData = (
cacheKey: string,
url: string,
signal: AbortSignal,
): Promise<LifecycleData> => {
const existing = lifecycleCache.get(cacheKey);
if (existing?.promise) {
return existing.promise;
}

const promise = coFetchJSON(url, 'GET', { signal }).then(
(result: LifecycleData) => {
lifecycleCache.set(cacheKey, {
data: result,
error: null,
timestamp: Date.now(),
});
return result;
},
(err: Error) => {
lifecycleCache.set(cacheKey, {
data: null,
error: err,
timestamp: Date.now(),
});
throw err;
},
);

lifecycleCache.set(cacheKey, {
data: null,
error: null,
timestamp: Date.now(),
promise,
});

return promise;
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't let one caller abort poison the shared cache entry.

The cached promise is created with the first caller's AbortSignal, then reused by later callers. If that original component unmounts or changes params, cleanup aborts the shared request, the catch path caches that abort as an error for 30s, and unrelated rows can show a false error/no-data state even though the backend is healthy.

Also applies to: 122-157

Comment thread go.mod
// 3.9.0+incompatible, but it was retracted.
replace github.com/openshift/api => github.com/openshift/api v0.0.0-20260114133223-6ab113cb7368

replace github.com/operator-framework/operator-registry => /Users/pegoncal/repos/perdasilva/operator-registry
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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the go.mod file around line 242 to verify the replace directives
echo "=== Checking go.mod around line 242 ==="
sed -n '235,250p' go.mod

echo -e "\n=== All replace directives in go.mod ==="
grep -n "^replace " go.mod || echo "No replace directives found"

echo -e "\n=== Checking for local paths in replace directives ==="
grep "replace.*=>" go.mod | grep -E "(\/Users|\/home|\.\/)" || echo "No local path replaces found"

Repository: openshift/console

Length of output: 1259


🏁 Script executed:

#!/bin/bash
# Check Docker-related replaces
echo "=== Docker-related module pins ==="
grep -n "docker" go.mod | grep -E "(github\.com/docker|replace)"

# Get line count to understand file size
echo -e "\n=== go.mod file info ==="
wc -l go.mod

Repository: openshift/console

Length of output: 662


Remove the workstation-local operator-registry replace before merge.

The absolute path at line 242 (/Users/pegoncal/repos/perdasilva/operator-registry) breaks reproducible builds and will fail in CI and for any downstream consumer unless they have that exact directory. Replace this with a reachable git ref or pseudo-version.

Note: Lines 244–246 pin Docker modules to exact versions (v27.5.0+incompatible) as required by coding guidelines, though these versions are older than the transitive dependencies would otherwise resolve. Verify this downgrade is intentional.

🤖 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 `@go.mod` at line 242, Remove the workstation-local replace entry for the
module "github.com/operator-framework/operator-registry" (which currently points
to an absolute path) and replace it with a reachable module reference such as an
official tagged version, a git tag/ref, or a pseudo-version (avoid absolute
filesystem paths); also verify the intentionality of the Docker module pins (the
pinned "v27.5.0+incompatible" entries) and either keep them with a comment
explaining why they must be downgraded or update them to the desired versions
consistent with transitive dependency resolution.

Comment thread pkg/olm/lifecycle.go
"net/http"
"regexp"

registryapi "github.com/operator-framework/operator-registry/pkg/api"
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the go.mod replace directive for operator-registry
grep -A2 "operator-framework/operator-registry" go.mod || echo "No replace directive found"

Repository: openshift/console

Length of output: 410


🏁 Script executed:

#!/bin/bash
# Check git log for any mention of this replace directive or the fork repo
git log --oneline --all -20 | head -20
# Also check for any UPSTREAM or TODO comments about this
grep -r "GetCustomSchemas\|operator-registry" --include="*.md" --include="*.txt" --include="*.go" | head -20

Repository: openshift/console

Length of output: 3844


🌐 Web query:

operator-framework operator-registry GetCustomSchemas status

💡 Result:

No evidence found of a GetCustomSchemas method or endpoint in the operator-framework/operator-registry. The operator-registry exposes a gRPC API via the api.Registry service with the following documented methods: ListPackages, GetPackage, GetBundle, GetBundleForChannel, GetBundleThatReplaces, GetChannelEntriesThatProvide, GetLatestChannelEntriesThatProvide, GetDefaultBundleThatProvides, GetChannelEntriesThatReplace[1][2][3][4][5]. Custom resource schemas are typically defined in CRDs within operator bundles, accessible via bundle manifests retrieved through GetBundle etc., not a dedicated GetCustomSchemas endpoint[6]. Recent schema work relates to bundle configuration validation (e.g., watchNamespace, deploymentConfig) in operator-controller, not registry API[7][8]. Status: no such API exists in current operator-registry (v1.66.0 as of 2026-04)[1].

Citations:


Remove local developer path from go.mod before merge—this blocks builds for all other developers.

The replace directive currently points to /Users/pegoncal/repos/perdasilva/operator-registry, a local machine-specific path that will fail in CI/CD and for other developers. Either point it to a public fork repository (with a GitHub URL) or remove the replace directive entirely.

Additionally, GetCustomSchemas doesn't exist in the upstream operator-registry API (confirmed as of v1.66.0). This is a fork-specific extension. If keeping the custom implementation, ensure the replace directive references a persistent, publicly accessible fork repository, and track the upstream work to eventually remove the replace directive when GetCustomSchemas is published.

🧰 Tools
🪛 golangci-lint (2.12.1)

[error] 9-9: could not import github.com/operator-framework/operator-registry/pkg/api (pkg/olm/lifecycle.go:9:2: github.com/operator-framework/operator-registry@v1.50.0: replacement directory /Users/pegoncal/repos/perdasilva/operator-registry does not exist)

(typecheck)

🤖 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 `@pkg/olm/lifecycle.go` at line 9, The go.mod replace directive pointing to a
local filesystem path must be removed or replaced with a persistent public fork
URL so CI and other devs can build; locate the replace entry in go.mod and
either delete it or change it to a public Git remote (e.g.,
github.com/<your-fork>/operator-registry vX.Y.Z) and run `go mod tidy`; also
reconcile use of the fork-only API GetCustomSchemas in lifecycle.go by either
removing or replacing calls to GetCustomSchemas (or wrapping them behind a
compat shim) if you intend to track upstream, or ensure the go.mod replace
references your public fork that actually implements GetCustomSchemas so the
symbol resolves.

@perdasilva perdasilva force-pushed the upgrade-planner-2.0 branch 2 times, most recently from b0bdcd9 to 31412d7 Compare May 12, 2026 08:13
@openshift-ci openshift-ci Bot added the component/shared Related to console-shared label May 12, 2026
@perdasilva perdasilva force-pushed the upgrade-planner-2.0 branch from 31412d7 to 28d960c Compare May 12, 2026 12:25
Adds lifecycle metadata (support status, OpenShift compatibility) to the
installed operators table by querying the CatalogSource gRPC Registry
service's ListPackageCustomSchemas endpoint.

Backend: New lifecycle handler dials the CatalogSource at
{catalogName}.{namespace}.svc:50051 and calls ListPackageCustomSchemas
with schema "io.openshift.operators.lifecycles.v1alpha1". CatalogSources
running older opm versions that don't implement the endpoint return 503,
so the frontend gracefully shows "no data" for those operators.

Frontend: New useOperatorLifecycle hook fetches lifecycle data from the
backend proxy. The installed operators table gains "Support Status" and
"OpenShift Compatibility" columns that display lifecycle phase and
version compatibility information from the catalog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@perdasilva perdasilva force-pushed the upgrade-planner-2.0 branch from 28d960c to 51f1a9e Compare May 12, 2026 15:26
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 do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/i18n Indicates issue or PR relates to internationalization or has content that needs to be translated

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant