Skip to content

feat: link to managed seed shoot and combine readiness#2834

Open
petersutter wants to merge 3 commits intomasterfrom
enh/link-to-managed-seed-shoot
Open

feat: link to managed seed shoot and combine readiness#2834
petersutter wants to merge 3 commits intomasterfrom
enh/link-to-managed-seed-shoot

Conversation

@petersutter
Copy link
Member

@petersutter petersutter commented Mar 13, 2026

What this PR does / why we need it:

  • Added Shoot column to seed list to link to shoot (in case of managed seed)
  • Introduced monitoring links (Plutono and Prometheus) for (managed) seeds, using advertisedAdresses
  • The Readiness column now shows the effective conditions
    • Seed: uses the Seed conditions.
    • Seed is Managed: uses the Shoot conditions, which provide more detail than the Seed conditions.

Which issue(s) this PR fixes:
Fixes #

Special notes for your reviewer:

Release note:


Summary by CodeRabbit

  • New Features

    • Added managed seeds and managed-seed-shoots API endpoints, real-time rooms/events, client stores, a Shoot link component, a Shoot column in seed lists, and Shoot monitoring links (Plutono/Prometheus).
  • Tests

    • Added extensive acceptance and unit tests covering API, IO, services, watches, sockets, and CORS/authorization flows.
  • Refactor

    • New composables/utilities for managed-seed-shoot data; removed legacy failure-tolerance / high-availability UI and related helpers.

@gardener-prow gardener-prow bot added do-not-merge/needs-kind Indicates a PR lacks a `kind/foo` label and requires one. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. cla: yes Indicates the PR's author has signed the cla-assistant.io CLA. labels Mar 13, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

Add end-to-end support for garden-scoped ManagedSeed and ManagedSeed-Shoot resources: cache accessors, services, routes, IO synchronize handlers and watches, authorization checks, RBAC, frontend stores/composables/components, router guard loading, socket wiring, and accompanying tests.

Changes

Cohort / File(s) Summary
Backend: cache & lookup
backend/lib/cache/index.js
New Cache APIs to list/get ManagedSeeds (by name, uid, garden namespace, and by associated shoot) and wired ManagedSeed into getByUid.
Backend: services & routes
backend/lib/services/managedseeds.js, backend/lib/services/managedseedshoots.js, backend/lib/services/authorization.js, backend/lib/services/index.js, backend/lib/routes/managedseeds.js, backend/lib/routes/managedseedshoots.js, backend/lib/routes/index.js
New services enforcing garden-only namespace + authorization; added authorization helpers; new REST routes and service exports for managed seeds and managed-seed-shoots.
Backend: IO / watches / dispatcher / hooks
backend/lib/io/managedseeds.js, backend/lib/io/managedseedshoots.js, backend/lib/io/dispatcher.js, backend/lib/io/helper.js, backend/lib/io/index.js, backend/lib/watches/managedseeds.js, backend/lib/watches/shoots.js, backend/lib/watches/index.js, backend/lib/hooks.js
Added synchronize handlers and dispatcher routing, new user profile flags for garden access, auto-join of garden rooms, managedseeds informer, publishing of managed-seed-shoot events and backfill behavior.
Backend: utils & RBAC
backend/lib/utils/index.js, charts/gardener-dashboard/charts/application/templates/dashboard/clusterrole.yaml
Added simplifyManagedSeed / simplifyManagedSeedShoot helpers and updated gardener-dashboard ClusterRole with list/watch on managedseeds.
Backend: tests
backend/__tests__/acceptance/api.managedseeds.spec.cjs, backend/__tests__/acceptance/io.cors.spec.cjs, backend/__tests__/acceptance/io.spec.cjs, backend/__tests__/services.managedseeds.spec.cjs, backend/__tests__/services.managedseedshoots.spec.cjs, backend/__tests__/hooks.spec.cjs, backend/__tests__/watches.spec.cjs
New/expanded unit and acceptance tests for services, routes, IO flows and watches; added garden-scoped cases, NotFound helper, and increased auth mock counts.
Frontend: API client & router guards
frontend/src/composables/useApi/api.js, frontend/src/router/guards.js, frontend/__tests__/composables/useApi.spec.js
Added API helpers for garden-managed endpoints and router-guard loading for admin users; tests updated to cover new endpoints.
Frontend: stores & socket integration
frontend/src/store/managedSeed.js, frontend/src/store/managedSeedShoot.js, frontend/src/store/socket/index.js, frontend/src/store/socket/helper.js
New Pinia stores for managed seeds and managed-seed-shoots, socket event handlers, integration into socket init and sync state.
Frontend: composables & utils
frontend/src/composables/useManagedSeedShootForSeed.js, frontend/src/composables/useSeedEffectiveConditions.js, frontend/src/composables/useShootAdvertisedAddresses.js, frontend/src/composables/useSeedItem/*, frontend/src/composables/useSeedTableSorting.js
New composables to resolve managed-seed-shoot data/URLs and effective conditions; removed several legacy failure-tolerance / HA helpers and adapted seed-item return shape and sorting key.
Frontend: components & UI
frontend/src/components/GManagedSeedShootLink.vue, frontend/src/components/GSeedListRow.vue, frontend/src/components/GSeedStatusTags.vue, frontend/src/components/SeedDetails/*, frontend/src/components/GShootListRow.vue
Added GManagedSeedShootLink, wired shoot link/UI elements and advertized-address tiles, refactored status/condition pipeline, removed high-availability UI pieces.
Frontend: views & other UI changes
frontend/src/views/*, frontend/src/components/SeedDetails/GSeedInfrastructureCard.vue, frontend/src/views/GSeedList.vue, frontend/src/views/GShootList.vue
Added SHOOT column to seed list and wiring for managed-seed providers in placeholders; removed high-availability column and related UI, adjusted selectedColumns init and related logic.
Frontend: tests
frontend/__tests__/*
Added/updated tests to cover new composables and API endpoints (e.g., managed-seed-shoots).

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Client
    participant Router as RouterGuard
    participant API as BackendAPI
    participant Auth as AuthService
    participant Cache as CacheStore
    participant Socket as Socket.IO

    Browser->>Router: navigate (seeds page, admin)
    Router->>API: GET /api/namespaces/garden/managedseeds
    API->>Auth: canListManagedSeedsInGardenNamespace(user)
    Auth-->>API: allowed
    API->>Cache: getManagedSeedsInGardenNamespace()
    Cache-->>API: [simplified managedSeed objects]
    API-->>Browser: 200 [simplified managed seeds]

    Note over Socket,Cache: realtime sync for garden-scoped resources
    Socket->>Cache: watch managedseeds / shoots (garden)
    Cache-->>Socket: ADDED/MODIFIED/DELETED events
    Socket-->>Browser: emit "managedseeds" / "managedseed-shoots" events
    Browser->>Browser: update managedSeedStore / UI

    Browser->>API: GET /api/namespaces/garden/managedseed-shoots
    API->>Auth: canListManagedSeedsInGardenNamespace && canListShootsInGardenNamespace
    Auth-->>API: allowed
    API->>Cache: getManagedSeedsInGardenNamespace() / getShootByName(...)
    Cache-->>API: shoot + status
    API-->>Browser: 200 [simplified managed-seed-shoots]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

approved, lgtm

Suggested reviewers

  • holgerkoser
  • klocke-io
  • grolu

Poem

🐰 I hopped through code and found a shoot,
I linked the seeds and traced each root,
With sockets singing and caches neat,
Garden shoots and seeds now gently meet,
A tiny hop — the dashboard feels complete!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main changes: adding a link to managed seed shoot and combining readiness conditions. It directly relates to the core functionality changes.
Description check ✅ Passed The description covers the main objectives of the PR: adding a Shoot column with links, introducing monitoring links for seeds, and updating the Readiness column to show effective conditions. All required sections are present.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch enh/link-to-managed-seed-shoot
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

@gardener-prow
Copy link

gardener-prow bot commented Mar 13, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign grolu 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

@petersutter petersutter added kind/enhancement Enhancement, improvement, extension and removed do-not-merge/needs-kind Indicates a PR lacks a `kind/foo` label and requires one. labels Mar 13, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (7)
frontend/__tests__/composables/useApi.spec.js (1)

36-52: Test looks good, but coverage for getManagedSeedsForGardenNamespace is missing.

The test correctly verifies the getManagedSeedShootsForGardenNamespace endpoint. Consider adding a similar test for getManagedSeedsForGardenNamespace to maintain consistent coverage for both new API functions.

💡 Suggested test for getManagedSeedsForGardenNamespace
describe('#getManagedSeedsForGardenNamespace', () => {
  it('should fetch managed seeds from the garden namespace endpoint', async () => {
    fetch.mockResponseOnce(JSON.stringify([]), {
      headers: {
        'content-type': 'application/json; charset=UTF-8',
      },
    })

    const res = await api.getManagedSeedsForGardenNamespace()

    expect(res.status).toBe(200)
    expect(res.data).toEqual([])
    expect(fetch).toBeCalledTimes(1)
    const [req] = fetch.mock.calls[0]
    expect(req.url).toBe('http://localhost:3000/api/namespaces/garden/managedseeds')
  })
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/__tests__/composables/useApi.spec.js` around lines 36 - 52, Add a
new test block mirroring the existing
describe('#getManagedSeedShootsForGardenNamespace') case to cover
getManagedSeedsForGardenNamespace: mock fetch response with
fetch.mockResponseOnce(JSON.stringify([]), { headers: { 'content-type':
'application/json; charset=UTF-8' } }), call
api.getManagedSeedsForGardenNamespace(), assert res.status === 200 and res.data
equals [], assert fetch called once, and assert the requested URL equals
'http://localhost:3000/api/namespaces/garden/managedseeds' so the new function
getManagedSeedsForGardenNamespace is covered.
frontend/src/store/managedSeedShoot.js (1)

50-52: Consider null-safety for shootByName when list is uninitialized.

The find function from lodash will return undefined when called with null as the collection, which is safe. However, if this behavior is intentional, it might be worth adding a brief comment or early return for clarity, especially since isInitial distinguishes the null state.

♻️ Optional: Add explicit null check for clarity
 function shootByName (namespace, name) {
+  if (list.value === null) {
+    return undefined
+  }
   return find(list.value, { metadata: { namespace, name } })
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/store/managedSeedShoot.js` around lines 50 - 52, The helper
shootByName currently calls find(list.value, { metadata: { namespace, name } })
which can receive null when list is uninitialized; add an explicit null-safety
guard in the shootByName function (check list.value or isInitial) and return
null/undefined early if the list is not ready so callers see the intent clearly;
reference the shootByName function and the list.value access and use an early
return to avoid relying on implicit lodash behavior.
backend/lib/watches/managedseeds.js (1)

22-24: Consider consistency in delete handler invocation.

The delete handler passes the deleted object as newObject:

informer.on('delete', object => handleEvent('DELETED', object))

While this works due to the newObject ?? oldObject fallback on line 13, semantically the deleted object is the "old" object. For consistency with the update handler pattern and clearer intent:

♻️ Optional: Pass deleted object as oldObject
 informer.on('add', object => handleEvent('ADDED', object))
 informer.on('update', (object, oldObject) => handleEvent('MODIFIED', object, oldObject))
-informer.on('delete', object => handleEvent('DELETED', object))
+informer.on('delete', object => handleEvent('DELETED', undefined, object))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/lib/watches/managedseeds.js` around lines 22 - 24, The delete handler
currently calls handleEvent('DELETED', object) which treats the deleted resource
as the "new" object; change it to pass the deleted resource as the oldObject
(i.e., call handleEvent('DELETED', undefined, object)) so it matches the update
pattern and the function signature of handleEvent(newObject, oldObject).
backend/__tests__/services.managedseedshoots.spec.cjs (1)

39-50: Add the inverse authorization case.

This only proves the canListManagedSeedsInGardenNamespace = false path. A regression where canListShootsInGardenNamespace stops being enforced would still pass this suite.

💡 Suggested test
   it('should reject unauthorized users', async () => {
     authorization.canListManagedSeedsInGardenNamespace.mockResolvedValue(false)

     await expect(managedseedshoots.list({ user, namespace: 'garden' })).rejects.toEqual(expect.objectContaining({
       statusCode: 403,
       message: 'You are not allowed to list managed seed shoots in the garden namespace',
     }))

     expect(authorization.canListManagedSeedsInGardenNamespace).toHaveBeenCalledWith(user)
     expect(authorization.canListShootsInGardenNamespace).toHaveBeenCalledWith(user)
     expect(cache.getManagedSeedsInGardenNamespace).not.toHaveBeenCalled()
   })
+
+  it('should reject users without shoot access', async () => {
+    authorization.canListShootsInGardenNamespace.mockResolvedValue(false)
+
+    await expect(managedseedshoots.list({ user, namespace: 'garden' })).rejects.toEqual(expect.objectContaining({
+      statusCode: 403,
+      message: 'You are not allowed to list managed seed shoots in the garden namespace',
+    }))
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/__tests__/services.managedseedshoots.spec.cjs` around lines 39 - 50,
Add a test that covers the inverse authorization path by simulating
canListManagedSeedsInGardenNamespace returning true but
canListShootsInGardenNamespace returning false, then call
managedseedshoots.list({ user, namespace: 'garden' }) and assert it rejects with
a 403 and the same message; also assert that
authorization.canListManagedSeedsInGardenNamespace was calledWith(user),
authorization.canListShootsInGardenNamespace was calledWith(user), and that
cache.getManagedSeedsInGardenNamespace was not called to ensure both checks are
enforced.
frontend/src/views/GSeedList.vue (1)

142-150: Sanitize persisted sort keys when the seed headers change.

This PR changes the available columns, but the view still restores seedSortBy from local storage verbatim on mount. A stale key from an older session can leave the table in an invalid sort state until the user manually resets it.

💡 Suggested follow-up
 onMounted(() => {
   updateTableSettings()
   if (seedSortBy.value?.length) {
-    sortBy.value = [...seedSortBy.value]
+    const validHeaderKeys = new Set(allHeaders.value.map(({ key }) => key))
+    const persistedSortBy = seedSortBy.value.filter(({ key }) => validHeaderKeys.has(key))
+    sortBy.value = persistedSortBy.length ? persistedSortBy : [{ key: 'name', order: 'asc' }]
+    seedSortBy.value = sortBy.value
   }
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/views/GSeedList.vue` around lines 142 - 150, Persisted
seedSortBy may reference column keys that no longer exist; when restoring
seedSortBy in GSeedList.vue, validate the saved key against the current headers
(the array with objects containing title/key/defaultSelected, e.g., the 'shoot'
header using getManagedSeedShootName). If the saved key is not found among
current header.key values, discard it and fall back to a safe default (the
header with defaultSelected true or an explicit fallback like 'name'), then
persist that sanitized value. Implement this check where seedSortBy/localStorage
is read on mount/setup so stale keys are never applied to the table.
backend/lib/io/helper.js (1)

70-81: Separate the garden-scoped permissions in req.user.profiles to preserve access control granularity.

The composite flag canGetManagedSeedAndShootInGardenNs uses AND logic, meaning a user with only canListManagedSeedsInGardenNamespace permission (without canListShootsInGardenNamespace) will be denied access to managed seeds in the IO layer. Both backend/lib/io/managedseeds.js and backend/lib/io/index.js check only the composite flag, which incorrectly locks out users who should have access to managed seeds alone.

Suggested change
     const profiles = Object.freeze({
       canListProjects,
       canListSeeds,
+      canListManagedSeedsInGardenNamespace,
+      canListShootsInGardenNamespace,
       canGetManagedSeedAndShootInGardenNs: canListManagedSeedsInGardenNamespace && canListShootsInGardenNamespace,
     })

Then update the access resolvers:

  • backend/lib/io/managedseeds.js:18 → check canListManagedSeedsInGardenNamespace alone
  • backend/lib/io/managedseedshoots.js:18 → check both permissions (keep composite or check both explicitly)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/lib/io/helper.js` around lines 70 - 81, The composite profile flag
canGetManagedSeedAndShootInGardenNs (created from
canListManagedSeedsInGardenNamespace && canListShootsInGardenNamespace in the
profiles object) incorrectly denies users who have only
canListManagedSeedsInGardenNamespace; change profiles to expose the two boolean
flags separately (keep canListManagedSeedsInGardenNamespace and
canListShootsInGardenNamespace) and remove or stop relying on the composite flag
for managed-seed-only checks; then update the access checks: in managedseeds.js
use canListManagedSeedsInGardenNamespace alone to authorize managed-seed access,
and in managedseedshoots.js continue to require both permissions (either by
checking the two flags explicitly or by using the composite where appropriate).
backend/__tests__/acceptance/io.spec.cjs (1)

566-571: Don’t preload access-review mocks in the JWT rejection tests.

These cases should prove authentication fails before any authorization check runs. Seeding four successful mockRequest responses here makes the tests pass even if expired or refresh-required tokens start hitting the authz backend.

♻️ Tighten the setup
   beforeEach(() => {
-    mockRequest
-      .mockImplementationOnce(fixtures.auth.mocks.reviewSelfSubjectAccess())
-      .mockImplementationOnce(fixtures.auth.mocks.reviewSelfSubjectAccess())
-      .mockImplementationOnce(fixtures.auth.mocks.reviewSelfSubjectAccess())
-      .mockImplementationOnce(fixtures.auth.mocks.reviewSelfSubjectAccess())
     timestamp = Math.floor(Date.now() / 1000)
   })

Add expect(mockRequest).not.toHaveBeenCalled() in the two tests below so they keep asserting the auth short-circuit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/__tests__/acceptance/io.spec.cjs` around lines 566 - 571, Remove the
preloaded authorization responses by deleting the four
mockImplementationOnce(...fixtures.auth.mocks.reviewSelfSubjectAccess()) calls
so the JWT rejection tests don't seed authz results; then, in the two
JWT-rejection test cases that assert short-circuit authentication failure, add
expect(mockRequest).not.toHaveBeenCalled() to ensure no authorization requests
were made. Target the mockRequest setup and the two tests that assert token
expiry/refresh-required behavior so they verify auth is short-circuited before
any call to fixtures.auth.mocks.reviewSelfSubjectAccess().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/__tests__/acceptance/io.spec.cjs`:
- Around line 566-571: Remove the preloaded authorization responses by deleting
the four
mockImplementationOnce(...fixtures.auth.mocks.reviewSelfSubjectAccess()) calls
so the JWT rejection tests don't seed authz results; then, in the two
JWT-rejection test cases that assert short-circuit authentication failure, add
expect(mockRequest).not.toHaveBeenCalled() to ensure no authorization requests
were made. Target the mockRequest setup and the two tests that assert token
expiry/refresh-required behavior so they verify auth is short-circuited before
any call to fixtures.auth.mocks.reviewSelfSubjectAccess().

In `@backend/__tests__/services.managedseedshoots.spec.cjs`:
- Around line 39-50: Add a test that covers the inverse authorization path by
simulating canListManagedSeedsInGardenNamespace returning true but
canListShootsInGardenNamespace returning false, then call
managedseedshoots.list({ user, namespace: 'garden' }) and assert it rejects with
a 403 and the same message; also assert that
authorization.canListManagedSeedsInGardenNamespace was calledWith(user),
authorization.canListShootsInGardenNamespace was calledWith(user), and that
cache.getManagedSeedsInGardenNamespace was not called to ensure both checks are
enforced.

In `@backend/lib/io/helper.js`:
- Around line 70-81: The composite profile flag
canGetManagedSeedAndShootInGardenNs (created from
canListManagedSeedsInGardenNamespace && canListShootsInGardenNamespace in the
profiles object) incorrectly denies users who have only
canListManagedSeedsInGardenNamespace; change profiles to expose the two boolean
flags separately (keep canListManagedSeedsInGardenNamespace and
canListShootsInGardenNamespace) and remove or stop relying on the composite flag
for managed-seed-only checks; then update the access checks: in managedseeds.js
use canListManagedSeedsInGardenNamespace alone to authorize managed-seed access,
and in managedseedshoots.js continue to require both permissions (either by
checking the two flags explicitly or by using the composite where appropriate).

In `@backend/lib/watches/managedseeds.js`:
- Around line 22-24: The delete handler currently calls handleEvent('DELETED',
object) which treats the deleted resource as the "new" object; change it to pass
the deleted resource as the oldObject (i.e., call handleEvent('DELETED',
undefined, object)) so it matches the update pattern and the function signature
of handleEvent(newObject, oldObject).

In `@frontend/__tests__/composables/useApi.spec.js`:
- Around line 36-52: Add a new test block mirroring the existing
describe('#getManagedSeedShootsForGardenNamespace') case to cover
getManagedSeedsForGardenNamespace: mock fetch response with
fetch.mockResponseOnce(JSON.stringify([]), { headers: { 'content-type':
'application/json; charset=UTF-8' } }), call
api.getManagedSeedsForGardenNamespace(), assert res.status === 200 and res.data
equals [], assert fetch called once, and assert the requested URL equals
'http://localhost:3000/api/namespaces/garden/managedseeds' so the new function
getManagedSeedsForGardenNamespace is covered.

In `@frontend/src/store/managedSeedShoot.js`:
- Around line 50-52: The helper shootByName currently calls find(list.value, {
metadata: { namespace, name } }) which can receive null when list is
uninitialized; add an explicit null-safety guard in the shootByName function
(check list.value or isInitial) and return null/undefined early if the list is
not ready so callers see the intent clearly; reference the shootByName function
and the list.value access and use an early return to avoid relying on implicit
lodash behavior.

In `@frontend/src/views/GSeedList.vue`:
- Around line 142-150: Persisted seedSortBy may reference column keys that no
longer exist; when restoring seedSortBy in GSeedList.vue, validate the saved key
against the current headers (the array with objects containing
title/key/defaultSelected, e.g., the 'shoot' header using
getManagedSeedShootName). If the saved key is not found among current header.key
values, discard it and fall back to a safe default (the header with
defaultSelected true or an explicit fallback like 'name'), then persist that
sanitized value. Implement this check where seedSortBy/localStorage is read on
mount/setup so stale keys are never applied to the table.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a9327e0-d28a-4e19-9c0d-b49d341a70a6

📥 Commits

Reviewing files that changed from the base of the PR and between ab79a2e and 3533048.

⛔ Files ignored due to path filters (2)
  • backend/__tests__/acceptance/__snapshots__/api.managedseeds.spec.cjs.snap is excluded by !**/*.snap
  • charts/__tests__/gardener-dashboard/application/dashboard/__snapshots__/clusterrole.spec.js.snap is excluded by !**/*.snap
📒 Files selected for processing (49)
  • backend/__tests__/acceptance/api.managedseeds.spec.cjs
  • backend/__tests__/acceptance/io.cors.spec.cjs
  • backend/__tests__/acceptance/io.spec.cjs
  • backend/__tests__/hooks.spec.cjs
  • backend/__tests__/services.managedseeds.spec.cjs
  • backend/__tests__/services.managedseedshoots.spec.cjs
  • backend/__tests__/watches.spec.cjs
  • backend/lib/cache/index.js
  • backend/lib/hooks.js
  • backend/lib/io/dispatcher.js
  • backend/lib/io/helper.js
  • backend/lib/io/index.js
  • backend/lib/io/managedseeds.js
  • backend/lib/io/managedseedshoots.js
  • backend/lib/routes/index.js
  • backend/lib/routes/managedseeds.js
  • backend/lib/routes/managedseedshoots.js
  • backend/lib/services/authorization.js
  • backend/lib/services/index.js
  • backend/lib/services/managedseeds.js
  • backend/lib/services/managedseedshoots.js
  • backend/lib/utils/index.js
  • backend/lib/watches/index.js
  • backend/lib/watches/managedseeds.js
  • backend/lib/watches/shoots.js
  • charts/gardener-dashboard/charts/application/templates/dashboard/clusterrole.yaml
  • frontend/__tests__/composables/useApi.spec.js
  • frontend/src/components/GManagedSeedShootLink.vue
  • frontend/src/components/GSeedListRow.vue
  • frontend/src/components/GSeedStatusTags.vue
  • frontend/src/components/GShootListRow.vue
  • frontend/src/components/SeedDetails/GSeedDetailsCard.vue
  • frontend/src/components/SeedDetails/GSeedMonitoringCard.vue
  • frontend/src/composables/useApi/api.js
  • frontend/src/composables/useManagedSeedShootForSeed.js
  • frontend/src/composables/useSeedEffectiveConditions.js
  • frontend/src/composables/useSeedItem/helper.js
  • frontend/src/composables/useSeedItem/index.js
  • frontend/src/composables/useSeedTableSorting.js
  • frontend/src/composables/useShootAdvertisedAddresses.js
  • frontend/src/router/guards.js
  • frontend/src/store/managedSeed.js
  • frontend/src/store/managedSeedShoot.js
  • frontend/src/store/socket/helper.js
  • frontend/src/store/socket/index.js
  • frontend/src/views/GSeedItemPlaceholder.vue
  • frontend/src/views/GSeedList.vue
  • frontend/src/views/GShootItemPlaceholder.vue
  • frontend/src/views/GShootList.vue
💤 Files with no reviewable changes (1)
  • frontend/src/composables/useSeedItem/helper.js

@petersutter petersutter force-pushed the enh/link-to-managed-seed-shoot branch from 3533048 to cea4f92 Compare March 13, 2026 15:27
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
frontend/src/components/SeedDetails/GSeedMonitoringCard.vue (1)

59-72: Consider adding icons for visual consistency.

The existing Plutono tile (line 44) specifies icon="mdi-developer-board", but the new Shoot Plutono and Shoot Prometheus tiles omit the icon prop. This creates visual inconsistency within the card.

If the omission is intentional (to visually subordinate these links), this is fine. Otherwise, consider adding appropriate icons.

♻️ Suggested change
       <g-link-list-tile
         v-if="managedSeedShootPlutonoUrl"
+        icon="mdi-developer-board"
         app-title="Shoot Plutono"
         :url="managedSeedShootPlutonoUrl"
         :url-text="managedSeedShootPlutonoUrl"
         content-class="pt-0"
       />
       <g-link-list-tile
         v-if="managedSeedShootPrometheusUrl"
+        icon="mdi-chart-line"
         app-title="Shoot Prometheus"
         :url="managedSeedShootPrometheusUrl"
         :url-text="managedSeedShootPrometheusUrl"
         content-class="pt-0"
       />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/SeedDetails/GSeedMonitoringCard.vue` around lines 59
- 72, The two g-link-list-tile components rendering Shoot Plutono and Shoot
Prometheus (using managedSeedShootPlutonoUrl and managedSeedShootPrometheusUrl)
omit the icon prop causing visual inconsistency with the existing Plutono tile;
update those components to pass an appropriate icon prop (e.g.,
icon="mdi-developer-board" or another suitable mdi-* icon) to match the other
tiles, or if omission was intentional, add a brief comment above each
g-link-list-tile explaining the deliberate visual subordination.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/lib/watches/shoots.js`:
- Around line 55-58: The current delete/update handler returns early when
cache.getManagedSeedForShootInGardenNamespace(name) yields no managedSeed,
causing missed DELETED events; change the logic so that when managedSeed is
undefined you still emit cleanup DELETED events to the
"managedseed-shoots;garden" clients based on a persisted tracking structure
(e.g., a previouslyPublishedManagedSeedShoots map keyed by shoot name or UID)
and then remove the entry from that tracking map; update the code around the
managedSeed variable usage to consult this map on missing lookups, publish the
DELETED event for previously-associated clients, and clear the tracked record so
stale entries cannot remain.

In `@frontend/src/composables/useSeedEffectiveConditions.js`:
- Around line 23-27: The code currently treats only undefined as missing and
returns managedSeedShootConditions.value even when it's null; update the check
in useSeedEffectiveConditions (or replace the conditional) so that null is
treated as missing too — e.g., check for non-nullish value
(managedSeedShootConditions.value != null) or use the nullish coalescing
behavior to return seedConditions.value when managedSeedShootConditions.value is
null or undefined, ensuring downstream rendering gets seedConditions.value
instead of null.

In `@frontend/src/views/GSeedList.vue`:
- Around line 142-150: The SHOOT column's sort key currently falls back to an
empty string which mismatches the displayed unmanaged label; update the value
fallback in the column definition (the object with title 'SHOOT' and value: item
=> getManagedSeedShootName(item) || '') to use 'Unmanaged' instead of '', and
make the same change wherever getManagedSeedShootName(item) is used for
sorting/display (the other occurrence referenced around lines 249-253) so
sorting/search uses the same 'Unmanaged' text shown to users.

---

Nitpick comments:
In `@frontend/src/components/SeedDetails/GSeedMonitoringCard.vue`:
- Around line 59-72: The two g-link-list-tile components rendering Shoot Plutono
and Shoot Prometheus (using managedSeedShootPlutonoUrl and
managedSeedShootPrometheusUrl) omit the icon prop causing visual inconsistency
with the existing Plutono tile; update those components to pass an appropriate
icon prop (e.g., icon="mdi-developer-board" or another suitable mdi-* icon) to
match the other tiles, or if omission was intentional, add a brief comment above
each g-link-list-tile explaining the deliberate visual subordination.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 273a679c-19fd-4769-a581-9865d2d88ece

📥 Commits

Reviewing files that changed from the base of the PR and between 3533048 and cea4f92.

⛔ Files ignored due to path filters (2)
  • backend/__tests__/acceptance/__snapshots__/api.managedseeds.spec.cjs.snap is excluded by !**/*.snap
  • charts/__tests__/gardener-dashboard/application/dashboard/__snapshots__/clusterrole.spec.js.snap is excluded by !**/*.snap
📒 Files selected for processing (50)
  • backend/__tests__/acceptance/api.managedseeds.spec.cjs
  • backend/__tests__/acceptance/io.cors.spec.cjs
  • backend/__tests__/acceptance/io.spec.cjs
  • backend/__tests__/hooks.spec.cjs
  • backend/__tests__/services.managedseeds.spec.cjs
  • backend/__tests__/services.managedseedshoots.spec.cjs
  • backend/__tests__/watches.spec.cjs
  • backend/lib/cache/index.js
  • backend/lib/hooks.js
  • backend/lib/io/dispatcher.js
  • backend/lib/io/helper.js
  • backend/lib/io/index.js
  • backend/lib/io/managedseeds.js
  • backend/lib/io/managedseedshoots.js
  • backend/lib/routes/index.js
  • backend/lib/routes/managedseeds.js
  • backend/lib/routes/managedseedshoots.js
  • backend/lib/services/authorization.js
  • backend/lib/services/index.js
  • backend/lib/services/managedseeds.js
  • backend/lib/services/managedseedshoots.js
  • backend/lib/utils/index.js
  • backend/lib/watches/index.js
  • backend/lib/watches/managedseeds.js
  • backend/lib/watches/shoots.js
  • charts/gardener-dashboard/charts/application/templates/dashboard/clusterrole.yaml
  • frontend/__tests__/composables/useApi.spec.js
  • frontend/src/components/GManagedSeedShootLink.vue
  • frontend/src/components/GSeedListRow.vue
  • frontend/src/components/GSeedStatusTags.vue
  • frontend/src/components/GShootListRow.vue
  • frontend/src/components/SeedDetails/GSeedDetailsCard.vue
  • frontend/src/components/SeedDetails/GSeedInfrastructureCard.vue
  • frontend/src/components/SeedDetails/GSeedMonitoringCard.vue
  • frontend/src/composables/useApi/api.js
  • frontend/src/composables/useManagedSeedShootForSeed.js
  • frontend/src/composables/useSeedEffectiveConditions.js
  • frontend/src/composables/useSeedItem/helper.js
  • frontend/src/composables/useSeedItem/index.js
  • frontend/src/composables/useSeedTableSorting.js
  • frontend/src/composables/useShootAdvertisedAddresses.js
  • frontend/src/router/guards.js
  • frontend/src/store/managedSeed.js
  • frontend/src/store/managedSeedShoot.js
  • frontend/src/store/socket/helper.js
  • frontend/src/store/socket/index.js
  • frontend/src/views/GSeedItemPlaceholder.vue
  • frontend/src/views/GSeedList.vue
  • frontend/src/views/GShootItemPlaceholder.vue
  • frontend/src/views/GShootList.vue
💤 Files with no reviewable changes (2)
  • frontend/src/composables/useSeedItem/helper.js
  • frontend/src/components/SeedDetails/GSeedInfrastructureCard.vue
🚧 Files skipped from review as they are similar to previous changes (29)
  • frontend/src/views/GShootList.vue
  • backend/lib/services/authorization.js
  • backend/lib/io/index.js
  • backend/lib/routes/managedseeds.js
  • backend/lib/io/managedseedshoots.js
  • backend/lib/routes/managedseedshoots.js
  • frontend/src/components/GShootListRow.vue
  • backend/lib/utils/index.js
  • backend/tests/watches.spec.cjs
  • frontend/src/components/GManagedSeedShootLink.vue
  • backend/lib/io/managedseeds.js
  • backend/tests/services.managedseedshoots.spec.cjs
  • frontend/src/store/socket/index.js
  • backend/lib/io/helper.js
  • frontend/src/store/socket/helper.js
  • backend/tests/acceptance/api.managedseeds.spec.cjs
  • backend/lib/watches/index.js
  • backend/lib/services/index.js
  • frontend/src/store/managedSeedShoot.js
  • frontend/src/store/managedSeed.js
  • frontend/src/composables/useManagedSeedShootForSeed.js
  • backend/lib/routes/index.js
  • frontend/tests/composables/useApi.spec.js
  • frontend/src/views/GSeedItemPlaceholder.vue
  • frontend/src/composables/useSeedItem/index.js
  • frontend/src/router/guards.js
  • frontend/src/composables/useShootAdvertisedAddresses.js
  • backend/lib/io/dispatcher.js
  • backend/lib/services/managedseedshoots.js

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/lib/watches/shoots.js`:
- Around line 49-71: publishManagedSeedShoots currently drops ADDED/MODIFIED
shoot events if cache.getManagedSeedForShootInGardenNamespace(name) returns
falsy, causing ordering/staleness; instead, stop treating the cache as
authoritative: either (A) always emit the shoot event to the
'managedseed-shoots;garden' room (like DELETED does) so clients receive the
initial ADDED/MODIFIED, or (B) implement a backfill in the ManagedSeed watcher:
in backend/lib/watches/managedseeds.js subscribe to ManagedSeed ADDED/MODIFIED
and when a ManagedSeed appears/changes, look up its shoot reference and emit the
corresponding managedseed-shoots message (use the same payload shape and the
publishManagedSeedShoots helper or a new emit helper) so associations discovered
after the Shoot event still notify clients; update publishManagedSeedShoots to
remove the early return (or record pending shoots) and ensure cache lookups are
only used for enrichment, not gating emission.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d636adb-c929-4dda-861f-b061f114b417

📥 Commits

Reviewing files that changed from the base of the PR and between cea4f92 and f2ae8cc.

📒 Files selected for processing (2)
  • backend/__tests__/watches.spec.cjs
  • backend/lib/watches/shoots.js

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/__tests__/watches.spec.cjs (1)

212-236: Consider verifying cache method call count.

The test correctly verifies the emitted events, but it could also assert that getManagedSeedForShootInGardenNamespace was called exactly 2 times (for ADD and UPDATE only, not DELETE) to fully verify the conditional logic in the production code.

💡 Optional enhancement
       expect(room.emit.mock.calls).toEqual([
         ['managedseed-shoots', { type: 'ADDED', uid: gardenManagedSeedShoot.metadata.uid }],
         ['managedseed-shoots', { type: 'MODIFIED', uid: gardenManagedSeedShoot.metadata.uid }],
         ['managedseed-shoots', { type: 'DELETED', uid: gardenManagedSeedShoot.metadata.uid }],
       ])
+
+      // Verify cache lookup was skipped for DELETE
+      expect(cache.getManagedSeedForShootInGardenNamespace).toHaveBeenCalledTimes(2)
     })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/__tests__/watches.spec.cjs` around lines 212 - 236, Add an assertion
to verify the cache call count: after emitting the events and awaiting
flushPromises in the "should emit managed seed shoots to a dedicated garden
room" test, assert that cache.getManagedSeedForShootInGardenNamespace was called
exactly 2 times (only for ADD and UPDATE). Use the existing mock name
getManagedSeedForShootInGardenNamespace and place the
expect(cache.getManagedSeedForShootInGardenNamespace).toHaveBeenCalledTimes(2)
alongside the other expects that check room.emit to ensure the conditional logic
is fully validated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/__tests__/watches.spec.cjs`:
- Around line 212-236: Add an assertion to verify the cache call count: after
emitting the events and awaiting flushPromises in the "should emit managed seed
shoots to a dedicated garden room" test, assert that
cache.getManagedSeedForShootInGardenNamespace was called exactly 2 times (only
for ADD and UPDATE). Use the existing mock name
getManagedSeedForShootInGardenNamespace and place the
expect(cache.getManagedSeedForShootInGardenNamespace).toHaveBeenCalledTimes(2)
alongside the other expects that check room.emit to ensure the conditional logic
is fully validated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd4a1f88-2045-4143-8b47-67e7e513f810

📥 Commits

Reviewing files that changed from the base of the PR and between f2ae8cc and 04e4db9.

📒 Files selected for processing (2)
  • backend/__tests__/watches.spec.cjs
  • backend/lib/watches/managedseeds.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/lib/watches/managedseeds.js

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

Labels

cla: yes Indicates the PR's author has signed the cla-assistant.io CLA. kind/enhancement Enhancement, improvement, extension size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant