Add operator lifecycle metadata to installed operators table#16551
Add operator lifecycle metadata to installed operators table#16551perdasilva wants to merge 7 commits into
Conversation
Adds lifecycle metadata (support status, cluster compatibility) to the
installed operators table by querying the CatalogSource catalog pod's
ExperimentalListPackageCustomSchemas gRPC endpoint on the
ExperimentalRegistry service.
Backend: New lifecycle handler dials the CatalogSource catalog pod at
{catalogName}.{namespace}.svc:50051 and calls
ExperimentalListPackageCustomSchemas with the x-acknowledge-experimental
metadata header and schema "io.openshift.operators.lifecycles.v1alpha1".
The gRPC call is wrapped in a 10-second timeout to prevent hanging on
unresponsive catalog pods. CatalogSources running older opm versions
that don't implement the endpoint return 503, so the frontend gracefully
shows "-" for those operators. Uses upstream
operator-framework/operator-registry v1.69.0.
Frontend: New useOperatorLifecycle hook fetches lifecycle data from the
backend proxy with client-side caching (5 min success, 30s error TTL)
and request deduplication. Abort errors are excluded from the cache to
prevent a single unmounting component from poisoning shared requests.
The installed operators table gains "Cluster Compatibility" and "Support"
columns that display platform compatibility and lifecycle phase
information from the catalog. Lifecycle data uses the
io.openshift.operators.lifecycles.v1alpha1 FBC schema with startDate/
endDate phase fields and platformCompatibility arrays. Date comparisons
use local-timezone boundaries to avoid off-by-one errors at phase edges.
Feature-gated behind the OPERATOR_LIFECYCLE_METADATA tech preview flag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…phase end date - Cluster compatibility: green check "Compatible" / red exclamation "Incompatible" - Support phase: neutral badge showing phase name from lifecycle data - Support phase ends: current phase end date in "MMM d, YYYY" format Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking the support phase badge now opens a popover showing all lifecycle phases with their end dates and a SKU disclaimer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: openshift/coderabbit/.coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughAdds backend lifecycle endpoint and frontend tech-preview feature to show operator cluster compatibility and support-phase in the installed-operators table, implements a cached lifecycle-fetch hook, UI components, tests, and Go dependency bumps. ChangesOperator Lifecycle Metadata Feature
Infrastructure and Dependencies
🎯 4 (Complex) | ⏱️ ~60 minutes Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (13 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: perdasilva The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/hold experimental |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (4)
pkg/serverutils/asynccache/asyncccache_test.go (1)
51-54: ⚖️ Poor tradeoffConsider potential flakiness from fixed sleep duration.
The 100ms sleep may be insufficient on heavily loaded or slow systems, potentially causing the test to capture the item before the first refresh completes. However, given that
wait.UntilWithContext(used inAsyncCache.Run) fires immediately and doesn't expose a completion signal, this fixed sleep is likely the most practical approach.Alternative: If flakiness is observed, consider using
Eventuallywith a short timeout:// wait for the first refresh to complete require.Eventually(t, func() bool { newItem := c.GetItem() return newItem != item }, 1*time.Second, 50*time.Millisecond, "first refresh should complete") item = c.GetItem()Though this would change the test logic, as it explicitly waits for a change rather than just ensuring the first refresh has fired.
🤖 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/serverutils/asynccache/asyncccache_test.go` around lines 51 - 54, The fixed 100ms sleep in the test may be flaky; update the test near the AsyncCache.Run usage to wait for the first refresh deterministically by polling instead of sleeping: replace the time.Sleep(100 * time.Millisecond) + item = c.GetItem() logic with a short Eventually-style loop that calls c.GetItem() until it observes a change (or times out), or use require.Eventually with a 1s timeout and 50ms interval to assert the first refresh completed; reference AsyncCache.Run / wait.UntilWithContext and c.GetItem to locate where to implement the polling/Eventually replacement.frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx (2)
812-816: 💤 Low valueuseMemo dependency array may be incomplete.
The
allHeadersmemo depends onallNamespaceActiveandlifecycleEnabled, but the header definitions also depend ont(the translation function). If the user changes language at runtime, headers won't re-translate until another dependency changes. This is a minor issue since language rarely changes mid-session.♻️ Add t to dependencies for completeness
const allHeaders = useMemo( () => (allNamespaceActive ? AllProjectsTableHeader() : SingleProjectTableHeader()), - // eslint-disable-next-line react-hooks/exhaustive-deps - [allNamespaceActive, lifecycleEnabled], + [allNamespaceActive, lifecycleEnabled, t], );Remove the eslint-disable comment if adding
t.🤖 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 812 - 816, The memo for allHeaders (created with useMemo) currently omits the translation function t from its dependency array so header labels won’t update when language changes; update the dependency array to include t (i.e., [allNamespaceActive, lifecycleEnabled, t]) and remove the eslint-disable-next-line react-hooks/exhaustive-deps comment, ensuring AllProjectsTableHeader and SingleProjectTableHeader will re-run when t changes.
725-728: 💤 Low valueKebab header lacks stable
idfor column management.All other headers now include an
idfield for column management integration, but the kebab action column header does not. While kebab columns are typically not hideable, the missingidcreates inconsistency with the updatedHeadertype definition (line 1609).♻️ Add id for consistency
const kebabHeader: Header = { + id: 'kebab', title: '', props: { className: KEBAB_COLUMN_CLASS }, };Alternatively, mark the kebab column as excluded from column management by adding
additional: falseif that pattern is used elsewhere.🤖 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 725 - 728, The kebabHeader constant lacks the stable `id` required by the updated Header type; update the kebabHeader object (the variable named kebabHeader in this file) to include a unique `id` string (e.g., "kebab" or "actions") so it matches other headers and works with column management, or alternatively add `additional: false` to explicitly exclude it from column management if that pattern is used elsewhere; ensure the change is applied to the kebabHeader declaration that currently sets title and props.frontend/packages/operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx (1)
180-182: 💤 Low valueInefficient double date conversion.
The code converts
endDateto a timestamp withparseLocalEndOfDay(p.endDate), then immediately wraps it innew Date()to format it. This double conversion (string→timestamp→Date) is unnecessary sinceformatDateaccepts a Date object directly.♻️ Simplify date handling
<DescriptionListDescription> - {formatDate(new Date(parseLocalEndOfDay(p.endDate)))} + {formatDate(new Date(p.endDate + 'T23:59:59'))} </DescriptionListDescription>Alternatively, add a helper that returns a Date directly:
const parseLocalEndOfDayAsDate = (dateStr: string): Date => { const [y, m, d] = dateStr.split('-').map(Number); return new Date(y, m - 1, d, 23, 59, 59, 999); };🤖 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 180 - 182, The code does an unnecessary double conversion by calling formatDate(new Date(parseLocalEndOfDay(p.endDate))); update usage to pass a Date directly (either change parseLocalEndOfDay to return a Date or add a new helper like parseLocalEndOfDayAsDate) and call formatDate(parseLocalEndOfDayAsDate(p.endDate)) in the DescriptionListDescription render; reference the existing symbols formatDate, parseLocalEndOfDay (or the new parseLocalEndOfDayAsDate) and p.endDate to locate and replace the conversion.
🤖 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
`@frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx`:
- Around line 381-388: Destructure the full return from useOperatorLifecycle
(e.g., const [lifecycleData, lifecycleLoading, lifecycleError] =
useOperatorLifecycle(...)) instead of only data, then propagate those states
into the lifecycle UI: when lifecycleLoading is true show a loading spinner/icon
in the lifecycle-related table cells (where compatible and supportPhase are
rendered) and when lifecycleError is present show an error indicator/message
rather than the static '-' placeholder; update any calls or UI logic that
compute compatible and supportPhase (getClusterCompatibility, getSupportPhase)
to use lifecycleData only when not loading/error so you don't compute against
undefined.
In
`@frontend/packages/operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx`:
- Around line 159-161: The formatDate function currently hardcodes the 'en-US'
locale, breaking i18n; update formatDate to use the user's locale instead—either
call toLocaleDateString without a locale (pass undefined) or read the active
locale from react-i18next (useTranslation()/i18n.language) and pass that value;
modify the formatDate implementation (function name: formatDate) to remove the
'en-US' literal and use the browser/default or i18n-provided locale so dates
respect user settings.
- Around line 84-92: The parsing functions parseLocalStartOfDay and
parseLocalEndOfDay currently assume a well-formed "YYYY-MM-DD" string; add
defensive validation: ensure dateStr.split('-') yields exactly 3 parts, convert
each part to Number and assert none are NaN (use Number.isFinite or
!Number.isNaN), validate month is 1–12 and day is 1–31 (optional stricter day
check per month), then construct the Date and verify resulting date.getTime() is
finite; if validation fails return NaN or throw a clear Error so callers can
handle malformed backend dates.
In
`@frontend/packages/operator-lifecycle-manager/src/hooks/useOperatorLifecycle.ts`:
- Around line 7-29: The lifecycleCache Map has no eviction and can grow
unbounded; add a bounded eviction policy (or periodic cleanup) to remove
stale/old entries: define a MAX_CACHE_SIZE constant and an evictOldEntries
function that sorts lifecycleCache entries by entry.timestamp and removes the
oldest until size <= MAX_CACHE_SIZE, and also add a periodic sweep that deletes
entries whose timestamp + TTL (use CACHE_TTL_SUCCESS/CACHE_TTL_ERROR depending
on entry.error) is expired; invoke evictOldEntries (and/or the sweep) after any
insertion into lifecycleCache (where entries are created using buildCacheKey and
LifecycleCacheEntry) and/or schedule it on an interval to prevent memory leaks
while keeping isCacheValid logic intact.
- Around line 31-42: The extractPackageName function currently calls JSON.parse
on the olmProperties string which may be untrusted; before parsing in
extractPackageName(olmProperties) validate that olmProperties is a string and
below a safe length (e.g., a configurable small max bytes) and return undefined
if it exceeds that limit or is not a string, then proceed to JSON.parse inside
the existing try/catch; this prevents expensive or maliciously large payloads
from being parsed while preserving the graceful undefined fallback handled by
the current catch.
- Around line 169-171: The getClusterVersion function currently relies on
optional chaining but should explicitly guard against missing SERVER_FLAGS:
update getClusterVersion to first check that window && typeof window !==
"undefined" and that window.SERVER_FLAGS is truthy (or use a null-check like if
(!window.SERVER_FLAGS) return undefined) before returning
window.SERVER_FLAGS.releaseVersion, ensuring the function safely handles
environments where SERVER_FLAGS is undefined and preserves the string |
undefined return type.
In `@go.mod`:
- Line 13: The go.mod currently pins a pre-release module
"github.com/golang/mock v1.7.0-rc.1"; replace this pre-release with a stable
release or the actively maintained fork. Locate the dependency entry for
github.com/golang/mock v1.7.0-rc.1 and either change it to the latest stable
semver (e.g., v1.6.x or the latest non-rc release) or replace the module path
with the maintained fork go.uber.org/mock at its current stable version; then
run go get / go mod tidy to update the lockfile and verify build/tests.
- Line 21: The go.mod currently pulls golang.org/x/net at a version flagged by
OSV (see golang.org/x/net); update the golang.org/x/net dependency in go.mod to
the patched release that fixes GO-2026-5025 through GO-2026-5030, then run
module resolution (e.g., bump via the Go tool and run tidy) to pin the fixed
version; finally run your full test/build matrix and verify compatibility across
the Kubernetes/operator-framework/api, gRPC and protobuf dependencies (rebuild,
run unit/integration tests and ensure no module version conflicts) and commit
the updated go.mod/go.sum.
In `@pkg/olm/lifecycle.go`:
- Around line 66-67: Replace the responses that include raw backend errors in
serverutils.SendResponse calls (e.g., the ApiError constructed with
fmt.Sprintf("failed to create gRPC client: %v", err) and the similar occurrence
around line 136) with a stable, generic client-facing message (for example
"internal server error" or "failed to create client") and do not include err
details in the ApiError payload; instead, log the full err server-side using the
existing logger or fmt/remote logger (referencing the same scope where the error
occurs) so internals stay out of the API contract.
- Line 69: The defer conn.Close() and the w.Write(jsonBytes) calls currently
ignore returned errors; change defer conn.Close() to capture and log its error
(e.g., defer func(){ if err := conn.Close(); err != nil {
logger.Errorf("conn.Close failed: %v", err) }}()) and handle the result of
w.Write(jsonBytes) by checking the returned (n, err) and logging or returning
the error from the function (e.g., if _, err := w.Write(jsonBytes); err != nil {
logger.Errorf("write response failed: %v", err); return }) so transport/close
failures from conn.Close() and w.Write(...) are observable. Ensure you use the
local logger/context used in this file for consistency and preserve existing
control flow in the function where conn.Close() and w.Write(jsonBytes) are used.
---
Nitpick comments:
In
`@frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx`:
- Around line 812-816: The memo for allHeaders (created with useMemo) currently
omits the translation function t from its dependency array so header labels
won’t update when language changes; update the dependency array to include t
(i.e., [allNamespaceActive, lifecycleEnabled, t]) and remove the
eslint-disable-next-line react-hooks/exhaustive-deps comment, ensuring
AllProjectsTableHeader and SingleProjectTableHeader will re-run when t changes.
- Around line 725-728: The kebabHeader constant lacks the stable `id` required
by the updated Header type; update the kebabHeader object (the variable named
kebabHeader in this file) to include a unique `id` string (e.g., "kebab" or
"actions") so it matches other headers and works with column management, or
alternatively add `additional: false` to explicitly exclude it from column
management if that pattern is used elsewhere; ensure the change is applied to
the kebabHeader declaration that currently sets title and props.
In
`@frontend/packages/operator-lifecycle-manager/src/components/operator-lifecycle-status.tsx`:
- Around line 180-182: The code does an unnecessary double conversion by calling
formatDate(new Date(parseLocalEndOfDay(p.endDate))); update usage to pass a Date
directly (either change parseLocalEndOfDay to return a Date or add a new helper
like parseLocalEndOfDayAsDate) and call
formatDate(parseLocalEndOfDayAsDate(p.endDate)) in the
DescriptionListDescription render; reference the existing symbols formatDate,
parseLocalEndOfDay (or the new parseLocalEndOfDayAsDate) and p.endDate to locate
and replace the conversion.
In `@pkg/serverutils/asynccache/asyncccache_test.go`:
- Around line 51-54: The fixed 100ms sleep in the test may be flaky; update the
test near the AsyncCache.Run usage to wait for the first refresh
deterministically by polling instead of sleeping: replace the time.Sleep(100 *
time.Millisecond) + item = c.GetItem() logic with a short Eventually-style loop
that calls c.GetItem() until it observes a change (or times out), or use
require.Eventually with a 1s timeout and 50ms interval to assert the first
refresh completed; reference AsyncCache.Run / wait.UntilWithContext and
c.GetItem to locate where to implement the polling/Eventually replacement.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: d430fc51-11ff-4d29-9db4-24f47e29148e
⛔ Files ignored due to path filters (284)
go.sumis excluded by!**/*.sumvendor/dario.cat/mergo/FUNDING.jsonis excluded by!**/vendor/**,!vendor/**vendor/dario.cat/mergo/README.mdis excluded by!**/vendor/**,!vendor/**vendor/dario.cat/mergo/SECURITY.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/AdaLogics/go-fuzz-headers/consumer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/ocb/ocb.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/antlrdoc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/atn.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/atn_config.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/input_stream.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/jcollect.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/lexer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/ll1_analyzer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/mutex.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/mutex_nomutex.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/parser_atn_simulator.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/prediction_context.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/recognizer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/statistics.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/token.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/antlr4-go/antlr/v4/utils.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/graphemes/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/graphemes/ansi.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/graphemes/ansi8.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/graphemes/reader.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/curve.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/conv/conv.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/integer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/point.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed448/ed448.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/sign.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/containerd/version/version.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/.golangci.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/compare.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/cpuinfo_linux.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/cpuinfo_other.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/database.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/defaults_windows.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/platform_compat_windows.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/platform_windows_compat.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/platforms.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/platforms_other.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/platforms/platforms_windows.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/client/command.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/go-connections/tlsconfig/certpool.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/go-connections/tlsconfig/config.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/go-connections/tlsconfig/config_client_ciphers.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/emicklei/go-restful/v3/.travis.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/emicklei/go-restful/v3/CHANGES.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/emicklei/go-restful/v3/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/emicklei/go-restful/v3/curly.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/emicklei/go-restful/v3/custom_verb.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/emicklei/go-restful/v3/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-billy/v5/fs.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-billy/v5/helper/chroot/chroot.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-billy/v5/memfs/memory.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-billy/v5/memfs/storage.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-billy/v5/osfs/os_bound.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-billy/v5/osfs/os_chroot.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/options.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/plumbing/object/commit.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/plumbing/transport/common.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/plumbing/transport/http/transport.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/auth_method.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/remote.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/repository.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/utils/merkletrie/index/node.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/worktree.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-git/go-git/v5/worktree_status.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/.cliff.tomlis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/.gitignoreis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/.golangci.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/NOTICEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/SECURITY.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/errors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonpointer/pointer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/.cliff.tomlis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/.editorconfigis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/.golangci.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/NOTICEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/SECURITY.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/internal/normalize_url.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/jsonreference/reference.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/.codecov.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/.golangci.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/.mockery.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/SECURITY.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/cmdutils/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/cmdutils/cmd_utils.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/cmdutils/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/cmdutils_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv/convert.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv/convert_types.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv/format.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv/sizeof.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv/type_constraints.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/conv_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/convert.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/convert_types.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/file.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/fileutils/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/fileutils/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/fileutils/file.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/fileutils/path.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/fileutils_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/go.workis excluded by!**/*.work,!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/go.work.sumis excluded by!**/*.sum,!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/initialism_index.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/json.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonname/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonname/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonname/name_provider.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonname_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/ifaces.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/registry_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/registry.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/concat.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/json.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils/ordered_map.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/jsonutils_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading/errors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading/json.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading/loading.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading/options.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading/yaml.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/loading_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/BENCHMARK.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/initialism_index.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/name_lexem.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/name_mangler.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/options.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/pools.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/split.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/string_bytes.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling/util.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/mangling_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/name_lexem.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/net.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/netutils/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/netutils/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/netutils/net.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/netutils_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/split.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/stringutils/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/stringutils/collection_formats.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/stringutils/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/stringutils/strings.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/stringutils_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/typeutils/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/typeutils/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/typeutils/types.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/typeutils_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/util.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/yaml.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/yamlutils/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/yamlutils/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/yamlutils/errors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/yamlutils/ordered_map.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/yamlutils/yaml.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/go-openapi/swag/yamlutils_iface.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/golang/mock/gomock/call.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/golang/mock/gomock/controller.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/golang/mock/gomock/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/BUILD.bazelis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/env.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/folding.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/library.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/optimizer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/program.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/templates/authoring.tmplis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/cel/validator.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/checker/checker.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/checker/env.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/checker/scopes.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/ast/ast.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/debug/debug.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/env/BUILD.bazelis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/env/env.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/BUILD.bazelis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/bool.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/bytes.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/double.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/duration.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/int.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/json_value.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/list.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/map.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/null.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/object.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/pb/type.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/string.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/timestamp.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/common/types/uint.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/interpreter/attribute_patterns.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/interpreter/attributes.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/interpreter/interpretable.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/interpreter/interpreter.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/interpreter/planner.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/cel-go/parser/helper.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/gnostic-models/extensions/extension.protois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.protois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.protois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/gnostic-models/openapiv3/annotations.protois excluded by!**/vendor/**,!vendor/**vendor/github.com/hashicorp/go-retryablehttp/.go-versionis excluded by!**/vendor/**,!vendor/**vendor/github.com/hashicorp/go-retryablehttp/.golangci.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/hashicorp/go-retryablehttp/CODEOWNERSis excluded by!**/vendor/**,!vendor/**vendor/github.com/hashicorp/go-retryablehttp/Makefileis excluded by!**/vendor/**,!vendor/**vendor/github.com/hashicorp/go-retryablehttp/client.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/josharian/intern/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/josharian/intern/intern.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/josharian/intern/license.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/.gitattributesis excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/.goreleaser.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/fse/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/fse/bitwriter.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/fse/compress.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/huff0/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/huff0/bitwriter.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/huff0/compress.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/huff0/decompress.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/huff0/decompress_amd64.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/huff0/decompress_generic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/huff0/huff0.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/internal/le/unsafe_disabled.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/internal/le/unsafe_enabled.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/internal/snapref/decode.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/internal/snapref/encode.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/bitwriter.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/blockdec.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/blockenc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/decoder.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/decoder_options.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/dict.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/enc_base.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/klauspost/compress/zstd/enc_best.gois excluded by!**/vendor/**,!vendor/**
📒 Files selected for processing (16)
frontend/packages/console-shared/src/constants/time.tsfrontend/packages/operator-lifecycle-manager/console-extensions.jsonfrontend/packages/operator-lifecycle-manager/locales/en/olm.jsonfrontend/packages/operator-lifecycle-manager/package.jsonfrontend/packages/operator-lifecycle-manager/src/components/__tests__/operator-lifecycle-status.spec.tsxfrontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsxfrontend/packages/operator-lifecycle-manager/src/components/operator-lifecycle-status.tsxfrontend/packages/operator-lifecycle-manager/src/const.tsfrontend/packages/operator-lifecycle-manager/src/features.tsfrontend/packages/operator-lifecycle-manager/src/hooks/useOperatorLifecycle.tsgo.modpkg/olm/handler.gopkg/olm/handler_test.gopkg/olm/lifecycle.gopkg/olm/lifecycle_test.gopkg/serverutils/asynccache/asyncccache_test.go
| const [lifecycleData] = useOperatorLifecycle( | ||
| lifecycleEnabled ? packageName : undefined, | ||
| catalogName, | ||
| catalogNamespace, | ||
| ); | ||
| const clusterVersion = getClusterVersion(); | ||
| const compatible = getClusterCompatibility(lifecycleData, version, clusterVersion); | ||
| const supportPhase = getSupportPhase(lifecycleData, version); |
There was a problem hiding this comment.
Loading and error states from lifecycle hook are ignored.
The useOperatorLifecycle hook returns [data, loading, error], but only the data is destructured. While loading or encountering errors, the UI displays - in the lifecycle columns, providing no feedback to users. This could be confusing when data is temporarily unavailable or the backend is slow.
💡 Proposed enhancement for loading/error states
const [lifecycleData] = useOperatorLifecycle(
+ const [lifecycleData, lifecycleLoading, lifecycleError] = useOperatorLifecycle(
lifecycleEnabled ? packageName : undefined,
catalogName,
catalogNamespace,
);
const clusterVersion = getClusterVersion();
- const compatible = getClusterCompatibility(lifecycleData, version, clusterVersion);
- const supportPhase = getSupportPhase(lifecycleData, version);
+ const compatible = lifecycleLoading ? 'no-data' : getClusterCompatibility(lifecycleData, version, clusterVersion);
+ const supportPhase = lifecycleLoading ? 'no-data' : getSupportPhase(lifecycleData, version);Or show a loading spinner icon in the table cells when lifecycleLoading is true.
🤖 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 381 - 388, Destructure the full return from useOperatorLifecycle
(e.g., const [lifecycleData, lifecycleLoading, lifecycleError] =
useOperatorLifecycle(...)) instead of only data, then propagate those states
into the lifecycle UI: when lifecycleLoading is true show a loading spinner/icon
in the lifecycle-related table cells (where compatible and supportPhase are
rendered) and when lifecycleError is present show an error indicator/message
rather than the static '-' placeholder; update any calls or UI logic that
compute compatible and supportPhase (getClusterCompatibility, getSupportPhase)
to use lifecycleData only when not loading/error so you don't compute against
undefined.
| const parseLocalStartOfDay = (dateStr: string): number => { | ||
| const [y, m, d] = dateStr.split('-').map(Number); | ||
| return new Date(y, m - 1, d).getTime(); | ||
| }; | ||
|
|
||
| const parseLocalEndOfDay = (dateStr: string): number => { | ||
| const [y, m, d] = dateStr.split('-').map(Number); | ||
| return new Date(y, m - 1, d, 23, 59, 59, 999).getTime(); | ||
| }; |
There was a problem hiding this comment.
Date parsing lacks input validation.
The parseLocalStartOfDay and parseLocalEndOfDay functions split the date string and convert to numbers without validating the input format. If the backend returns malformed dates (e.g., missing components, wrong separator), split('-').map(Number) will produce NaN values, resulting in Invalid Date objects and incorrect timestamp calculations.
🛡️ Proposed fix to add validation
const parseLocalStartOfDay = (dateStr: string): number => {
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
+ return NaN;
+ }
const [y, m, d] = dateStr.split('-').map(Number);
+ if (!y || !m || !d) {
+ return NaN;
+ }
return new Date(y, m - 1, d).getTime();
};
const parseLocalEndOfDay = (dateStr: string): number => {
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
+ return NaN;
+ }
const [y, m, d] = dateStr.split('-').map(Number);
+ if (!y || !m || !d) {
+ return NaN;
+ }
return new Date(y, m - 1, d, 23, 59, 59, 999).getTime();
};Callers should also check for NaN results, or wrap date operations in try-catch.
📝 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.
| const parseLocalStartOfDay = (dateStr: string): number => { | |
| const [y, m, d] = dateStr.split('-').map(Number); | |
| return new Date(y, m - 1, d).getTime(); | |
| }; | |
| const parseLocalEndOfDay = (dateStr: string): number => { | |
| const [y, m, d] = dateStr.split('-').map(Number); | |
| return new Date(y, m - 1, d, 23, 59, 59, 999).getTime(); | |
| }; | |
| const parseLocalStartOfDay = (dateStr: string): number => { | |
| if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { | |
| return NaN; | |
| } | |
| const [y, m, d] = dateStr.split('-').map(Number); | |
| if (!y || !m || !d) { | |
| return NaN; | |
| } | |
| return new Date(y, m - 1, d).getTime(); | |
| }; | |
| const parseLocalEndOfDay = (dateStr: string): number => { | |
| if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { | |
| return NaN; | |
| } | |
| const [y, m, d] = dateStr.split('-').map(Number); | |
| if (!y || !m || !d) { | |
| return NaN; | |
| } | |
| return new Date(y, m - 1, d, 23, 59, 59, 999).getTime(); | |
| }; |
🤖 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 84 - 92, The parsing functions parseLocalStartOfDay and
parseLocalEndOfDay currently assume a well-formed "YYYY-MM-DD" string; add
defensive validation: ensure dateStr.split('-') yields exactly 3 parts, convert
each part to Number and assert none are NaN (use Number.isFinite or
!Number.isNaN), validate month is 1–12 and day is 1–31 (optional stricter day
check per month), then construct the Date and verify resulting date.getTime() is
finite; if validation fails return NaN or throw a clear Error so callers can
handle malformed backend dates.
| const formatDate = (date: Date): string => { | ||
| return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); | ||
| }; |
There was a problem hiding this comment.
Hardcoded locale breaks internationalization.
The formatDate function hardcodes the 'en-US' locale, which conflicts with the i18n infrastructure already in place (react-i18next). Users in different locales will see dates in US format regardless of their locale settings.
🌍 Proposed fix to respect user locale
const formatDate = (date: Date): string => {
- return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
+ return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
};Passing undefined as the locale uses the user's browser locale automatically.
📝 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.
| const formatDate = (date: Date): string => { | |
| return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); | |
| }; | |
| const formatDate = (date: Date): string => { | |
| return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }); | |
| }; |
🤖 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 159 - 161, The formatDate function currently hardcodes the 'en-US'
locale, breaking i18n; update formatDate to use the user's locale instead—either
call toLocaleDateString without a locale (pass undefined) or read the active
locale from react-i18next (useTranslation()/i18n.language) and pass that value;
modify the formatDate implementation (function name: formatDate) to remove the
'en-US' literal and use the browser/default or i18n-provided locale so dates
respect user settings.
| const CACHE_TTL_SUCCESS = 5 * 60 * 1000; // 5 minutes | ||
| const CACHE_TTL_ERROR = 30 * 1000; // 30 seconds | ||
|
|
||
| type LifecycleCacheEntry = { | ||
| data: LifecycleData | null; | ||
| error: Error | null; | ||
| timestamp: number; | ||
| promise?: Promise<LifecycleData>; | ||
| }; | ||
|
|
||
| const lifecycleCache = new Map<string, LifecycleCacheEntry>(); | ||
|
|
||
| const buildCacheKey = (catalogNamespace: string, catalogName: string, packageName: string) => | ||
| `${catalogNamespace}/${catalogName}/${packageName}`; | ||
|
|
||
| const isCacheValid = (entry: LifecycleCacheEntry): boolean => { | ||
| if (entry.promise) { | ||
| return true; | ||
| } | ||
| const age = Date.now() - entry.timestamp; | ||
| const ttl = entry.error ? CACHE_TTL_ERROR : CACHE_TTL_SUCCESS; | ||
| return age < ttl; | ||
| }; |
There was a problem hiding this comment.
Cache grows unbounded leading to potential memory leak.
The lifecycleCache Map has no eviction policy. While entries have TTL checks in isCacheValid, stale entries are never removed from the Map itself. Over time (e.g., long-running console sessions visiting many operators), the cache will accumulate expired entries, consuming memory unnecessarily.
Consider adding periodic cleanup or size-based eviction:
const MAX_CACHE_SIZE = 100;
const evictOldEntries = () => {
if (lifecycleCache.size > MAX_CACHE_SIZE) {
const entries = Array.from(lifecycleCache.entries());
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
const toDelete = entries.slice(0, entries.length - MAX_CACHE_SIZE);
toDelete.forEach(([key]) => lifecycleCache.delete(key));
}
};
// Call evictOldEntries() after each cache insertionOr use a proper LRU cache library if available in the project.
🤖 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 7 - 29, The lifecycleCache Map has no eviction and can grow
unbounded; add a bounded eviction policy (or periodic cleanup) to remove
stale/old entries: define a MAX_CACHE_SIZE constant and an evictOldEntries
function that sorts lifecycleCache entries by entry.timestamp and removes the
oldest until size <= MAX_CACHE_SIZE, and also add a periodic sweep that deletes
entries whose timestamp + TTL (use CACHE_TTL_SUCCESS/CACHE_TTL_ERROR depending
on entry.error) is expired; invoke evictOldEntries (and/or the sweep) after any
insertion into lifecycleCache (where entries are created using buildCacheKey and
LifecycleCacheEntry) and/or schedule it on an interval to prevent memory leaks
while keeping isCacheValid logic intact.
| const extractPackageName = (olmProperties: string): string | undefined => { | ||
| try { | ||
| const props = JSON.parse(olmProperties); | ||
| const packageProp = props.find( | ||
| (p: { type: string; value: { packageName?: string } }) => | ||
| p.type === 'olm.package' && p.value?.packageName, | ||
| ); | ||
| return packageProp?.value?.packageName; | ||
| } catch { | ||
| return undefined; | ||
| } | ||
| }; |
There was a problem hiding this comment.
JSON.parse on untrusted annotation data could throw.
The extractPackageName function parses the olm.properties annotation using JSON.parse with a try-catch. While the catch block returns undefined, this assumes annotations are benign. If annotations can be controlled by external operators or untrusted sources, malformed or malicious JSON could cause denial-of-service through excessive parsing time or throw unexpected errors.
The current try-catch already handles parse failures gracefully. However, ensure OLM annotations are validated at ingestion time, or add a size check before parsing:
const extractPackageName = (olmProperties: string): string | undefined => {
+ if (!olmProperties || olmProperties.length > 10000) {
+ return undefined;
+ }
try {
const props = JSON.parse(olmProperties);Based on learnings from similar annotation parsing patterns in the codebase.
🤖 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 31 - 42, The extractPackageName function currently calls JSON.parse
on the olmProperties string which may be untrusted; before parsing in
extractPackageName(olmProperties) validate that olmProperties is a string and
below a safe length (e.g., a configurable small max bytes) and return undefined
if it exceeds that limit or is not a string, then proceed to JSON.parse inside
the existing try/catch; this prevents expensive or maliciously large payloads
from being parsed while preserving the graceful undefined fallback handled by
the current catch.
| export const getClusterVersion = (): string | undefined => { | ||
| return window.SERVER_FLAGS?.releaseVersion; | ||
| }; |
There was a problem hiding this comment.
Potential null dereference on window.SERVER_FLAGS.
The getClusterVersion function accesses window.SERVER_FLAGS?.releaseVersion using optional chaining on the first property, but window.SERVER_FLAGS itself is not checked for existence. In environments where SERVER_FLAGS is undefined (e.g., tests, certain build configurations), this will return undefined as expected. However, explicit defensive checks improve clarity.
🛡️ Proposed fix for explicit null safety
export const getClusterVersion = (): string | undefined => {
- return window.SERVER_FLAGS?.releaseVersion;
+ return typeof window !== 'undefined' ? window.SERVER_FLAGS?.releaseVersion : undefined;
};Or if window is guaranteed in browser context:
export const getClusterVersion = (): string | undefined => {
- return window.SERVER_FLAGS?.releaseVersion;
+ return window?.SERVER_FLAGS?.releaseVersion;
};🤖 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 169 - 171, The getClusterVersion function currently relies on
optional chaining but should explicitly guard against missing SERVER_FLAGS:
update getClusterVersion to first check that window && typeof window !==
"undefined" and that window.SERVER_FLAGS is truthy (or use a null-check like if
(!window.SERVER_FLAGS) return undefined) before returning
window.SERVER_FLAGS.releaseVersion, ensuring the function safely handles
environments where SERVER_FLAGS is undefined and preserves the string |
undefined return type.
| github.com/devfile/registry-support/index/generator v0.0.0-20240419194226-cca4c9a81f8d | ||
| github.com/devfile/registry-support/registry-library v0.0.0-20240521161747-89fc566cb024 | ||
| github.com/golang/mock v1.6.0 | ||
| github.com/golang/mock v1.7.0-rc.1 |
There was a problem hiding this comment.
Remove pre-release version from production dependencies.
github.com/golang/mock v1.7.0-rc.1 is a release candidate (pre-release version). As per coding guidelines, pre-release versions should not be used in production.
🔧 Recommended fix
Either pin to the latest stable release of github.com/golang/mock or use the maintained fork go.uber.org/mock (the original repository is archived):
- github.com/golang/mock v1.7.0-rc.1
+ go.uber.org/mock v0.5.0Note: golang/mock is archived; go.uber.org/mock is the actively maintained fork.
As per coding guidelines: "No pre-release or yanked versions in production" for supply chain security.
📝 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.
| github.com/golang/mock v1.7.0-rc.1 | |
| go.uber.org/mock v0.5.0 |
🤖 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 13, The go.mod currently pins a pre-release module
"github.com/golang/mock v1.7.0-rc.1"; replace this pre-release with a stable
release or the actively maintained fork. Locate the dependency entry for
github.com/golang/mock v1.7.0-rc.1 and either change it to the latest stable
semver (e.g., v1.6.x or the latest non-rc release) or replace the module path
with the maintained fork go.uber.org/mock at its current stable version; then
run go get / go mod tidy to update the lockfile and verify build/tests.
| github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13 | ||
| github.com/openshift/library-go v0.0.0-20231020125034-5a2d9fe760b3 | ||
| github.com/operator-framework/api v0.30.0 | ||
| github.com/operator-framework/api v0.42.0 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check for CVEs in updated Go dependencies
# List of updated direct dependencies to check
PACKAGES=(
"github.com/operator-framework/api@v0.42.0"
"github.com/operator-framework/operator-registry@v1.69.0"
"github.com/prometheus/common@v0.67.5"
"golang.org/x/mod@v0.36.0"
"golang.org/x/net@v0.54.0"
"golang.org/x/oauth2@v0.36.0"
"google.golang.org/grpc@v1.81.1"
"google.golang.org/protobuf@v1.36.11"
"k8s.io/api@v0.35.4"
"k8s.io/apiextensions-apiserver@v0.35.4"
"k8s.io/apimachinery@v0.35.4"
"k8s.io/client-go@v0.35.4"
"k8s.io/utils@v0.0.0-20260108192941-914a6e750570"
"sigs.k8s.io/controller-runtime@v0.23.3"
)
echo "Checking for known vulnerabilities..."
for pkg in "${PACKAGES[@]}"; do
echo "Checking $pkg"
# Query OSV database for vulnerabilities
curl -s -X POST "https://api.osv.dev/v1/query" \
-H "Content-Type: application/json" \
-d "{\"package\": {\"name\": \"${pkg%@*}\", \"ecosystem\": \"Go\"}, \"version\": \"${pkg#*@}\"}" | \
jq -r '.vulns[]? | " CVE: \(.id) - \(.summary)"'
done
echo -e "\nChecking if versions exist in proxy..."
for pkg in "${PACKAGES[@]}"; do
module="${pkg%@*}"
version="${pkg#*@}"
# Check Go module proxy
status=$(curl -s -o /dev/null -w "%{http_code}" "https://proxy.golang.org/${module}/@v/${version}.info")
if [ "$status" != "200" ]; then
echo "WARNING: $pkg returned HTTP $status (may be yanked or invalid)"
else
echo "✓ $pkg exists"
fi
doneRepository: openshift/console
Length of output: 1993
Address OSV-flagged CVEs in updated golang.org/x/net
The vulnerability scan of the updated direct dependencies reports multiple OSV advisories for golang.org/x/net@v0.54.0 (GO-2026-5025 through GO-2026-5030; issues include HTML parsing/DoS and potential XSS in golang.org/x/net/html). Update golang.org/x/net to a fixed version and confirm compatibility across the Kubernetes/operator/grpc/protobuf stack.
All checked versions (including golang.org/x/net@v0.54.0) exist on proxy.golang.org (HTTP 200), so this is not a yanked/invalid-version problem.
🤖 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 21, The go.mod currently pulls golang.org/x/net at a version
flagged by OSV (see golang.org/x/net); update the golang.org/x/net dependency in
go.mod to the patched release that fixes GO-2026-5025 through GO-2026-5030, then
run module resolution (e.g., bump via the Go tool and run tidy) to pin the fixed
version; finally run your full test/build matrix and verify compatibility across
the Kubernetes/operator-framework/api, gRPC and protobuf dependencies (rebuild,
run unit/integration tests and ensure no module version conflicts) and commit
the updated go.mod/go.sum.
| serverutils.SendResponse(w, http.StatusInternalServerError, serverutils.ApiError{Err: fmt.Sprintf("failed to create gRPC client: %v", err)}) | ||
| return |
There was a problem hiding this comment.
Avoid exposing backend internals in API error payloads.
Line 66 and Line 136 return raw transport/backend error details to the client. This leaks internal implementation details and creates an unstable public error contract. Return a stable generic message to clients and keep specifics in server logs.
Suggested hardening
- serverutils.SendResponse(w, http.StatusInternalServerError, serverutils.ApiError{Err: fmt.Sprintf("failed to create gRPC client: %v", err)})
+ serverutils.SendResponse(w, http.StatusInternalServerError, serverutils.ApiError{Err: "failed to connect to catalog source"})
...
- serverutils.SendResponse(w, http.StatusBadGateway, serverutils.ApiError{Err: fmt.Sprintf("catalog source error: %s", st.Message())})
+ serverutils.SendResponse(w, http.StatusBadGateway, serverutils.ApiError{Err: "catalog source error"})Also applies to: 136-136
🤖 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 66 - 67, Replace the responses that
include raw backend errors in serverutils.SendResponse calls (e.g., the ApiError
constructed with fmt.Sprintf("failed to create gRPC client: %v", err) and the
similar occurrence around line 136) with a stable, generic client-facing message
(for example "internal server error" or "failed to create client") and do not
include err details in the ApiError payload; instead, log the full err
server-side using the existing logger or fmt/remote logger (referencing the same
scope where the error occurs) so internals stay out of the API contract.
| serverutils.SendResponse(w, http.StatusInternalServerError, serverutils.ApiError{Err: fmt.Sprintf("failed to create gRPC client: %v", err)}) | ||
| return | ||
| } | ||
| defer conn.Close() |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la
rg -n "conn\.Close\(" -S pkg/olm/lifecycle.go || true
rg -n "\bw\.Write\(" -S pkg/olm/lifecycle.go || true
echo "---- context around conn.Close ----"
sed -n '40,95p' pkg/olm/lifecycle.go
echo "---- context around w.Write ----"
sed -n '95,160p' pkg/olm/lifecycle.goRepository: openshift/console
Length of output: 8123
Handle errors returned by conn.Close() and w.Write(...)
defer conn.Close() drops any close error, and w.Write(jsonBytes) discards the write error. Log (or otherwise handle) both so transport failures are observable.
Suggested fix
- defer conn.Close()
+ defer func() {
+ if cerr := conn.Close(); cerr != nil {
+ klog.Warningf("[lifecycle] Failed to close gRPC client for %s: %v", target, cerr)
+ }
+ }()
...
- w.Write(jsonBytes)
+ if _, err := w.Write(jsonBytes); err != nil {
+ klog.Errorf("[lifecycle] Failed to write lifecycle response body: %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` at line 69, The defer conn.Close() and the
w.Write(jsonBytes) calls currently ignore returned errors; change defer
conn.Close() to capture and log its error (e.g., defer func(){ if err :=
conn.Close(); err != nil { logger.Errorf("conn.Close failed: %v", err) }}()) and
handle the result of w.Write(jsonBytes) by checking the returned (n, err) and
logging or returning the error from the function (e.g., if _, err :=
w.Write(jsonBytes); err != nil { logger.Errorf("write response failed: %v",
err); return }) so transport/close failures from conn.Close() and w.Write(...)
are observable. Ensure you use the local logger/context used in this file for
consistency and preserve existing control flow in the function where
conn.Close() and w.Write(jsonBytes) are used.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use appendTo="inline" so the popover stays anchored to its trigger element through table re-renders. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@perdasilva: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions 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. |
Summary
OPERATOR_LIFECYCLE_METADATAfeature flag): Cluster compatibility (green check / red exclamation), Support phase (neutral badge with phase name), and Support phase ends (end date in "MMM d, YYYY" format)/api/olm/lifecycle/with in-memory caching (5 min TTL)Analysis / Root cause:
The Installed Operators table did not surface operator lifecycle metadata (cluster compatibility, support phase, and support end dates), making it difficult for cluster administrators to assess operator upgrade readiness and support status at a glance.
Solution description:
useOperatorLifecyclehook that fetches lifecycle data from/api/olm/lifecycle/{namespace}/{catalog}/{package}with request deduplication and cachinggetClusterCompatibility()andgetSupportPhase()utility functions to compute compatibility and support status from lifecycle dataClusterCompatibilityStatus,SupportPhaseBadge,SupportPhaseEndDatecomponents for rendering the three new columnsLifecycleDatesPopovercomponent that displays all lifecycle phases and end dates when clicking the support phase badgeScreenshots / screen recording:
Test setup:
Requires a cluster with the
OPERATOR_LIFECYCLE_METADATAfeature flag enabled and operators with lifecycle metadata available via the OLM lifecycle API.Test cases:
Browser conformance:
Additional info:
Unit tests added for all new components and utility functions (29 tests passing).
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Localization
Tests