Skip to content

feat(controller): create prerequisites from annotation - #227

Open
rdwj wants to merge 3 commits into
kubernetes-sigs:mainfrom
rdwj:feat/auto-create-prerequisites
Open

feat(controller): create prerequisites from annotation#227
rdwj wants to merge 3 commits into
kubernetes-sigs:mainfrom
rdwj:feat/auto-create-prerequisites

Conversation

@rdwj

@rdwj rdwj commented Jun 4, 2026

Copy link
Copy Markdown

Summary

When the annotation mcp.x-k8s.io/auto-create-prerequisites is set to "true" on an MCPServer CR, the operator creates missing ServiceAccounts and ConfigMaps before validation. Resources are owned by the MCPServer and cleaned up on deletion.

Motivation

Deploying MCP servers from the RHOAI catalog requires manually creating ServiceAccounts and ConfigMaps per namespace before the MCPServer CR will reconcile. This annotation-gated feature automates that step for catalog-based deployments.

Addresses #226

Changes

  • New file: internal/controller/mcpserver_controller_prerequisites.goensurePrerequisites() function
  • Modified: internal/controller/mcpserver_controller.go — call ensurePrerequisites() before validation, updated RBAC markers
  • Updated: config/rbac/role.yaml — added list, watch, and create verbs for serviceaccounts
  • Tests: internal/controller/mcpserver_controller_prerequisites_test.go — 7 test cases covering annotation gating, resource creation, idempotency, and owner references

Testing

Validated on a live RHOAI 3.4 / OpenShift 4.20 cluster:

  • With annotation: ServiceAccount and ConfigMap auto-created, MCPServer reconciled to Running
  • Without annotation: no resources created, MCPServer correctly reported Failed with missing ConfigMap error
  • All existing unit tests pass (make test)
  • Lint clean (make lint)

Summary by CodeRabbit

  • New Features

    • MCPServer controller now automatically provisions prerequisite ServiceAccounts and ConfigMaps when configured via the mcp.x-k8s.io/auto-create-prerequisites annotation.
  • Chores

    • Updated RBAC permissions to support automatic prerequisite resource creation.

@k8s-ci-robot k8s-ci-robot added the do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. label Jun 4, 2026
@netlify

netlify Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploy Preview for mcp-lifecycle-operator ready!

Name Link
🔨 Latest commit 6aef031
🔍 Latest deploy log https://app.netlify.com/projects/mcp-lifecycle-operator/deploys/6a28d6a15ba13f0008520843
😎 Deploy Preview https://deploy-preview-227--mcp-lifecycle-operator.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@k8s-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

@linux-foundation-easycla

linux-foundation-easycla Bot commented Jun 4, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: rdwj / name: rdwj (d7cb3fc)

@k8s-ci-robot
k8s-ci-robot requested review from matzew and soltysh June 4, 2026 03:43
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

Welcome @rdwj!

It looks like this is your first PR to kubernetes-sigs/mcp-lifecycle-operator 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-sigs/mcp-lifecycle-operator has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@k8s-ci-robot k8s-ci-robot added the cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. label Jun 4, 2026
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

Hi @rdwj. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels Jun 4, 2026
rdwj added 2 commits June 3, 2026 23:09
When the annotation mcp.x-k8s.io/auto-create-prerequisites is set to
"true", the operator creates missing ServiceAccounts and ConfigMaps
referenced by the MCPServer CR before validation. Resources are owned
by the MCPServer so they are cleaned up on deletion.

Related: kubernetes-sigs#226
Controller-runtime's client cache requires list and watch permissions
to set up informers. Without them, the ensurePrerequisites function
cannot check ServiceAccount existence.
@rdwj
rdwj force-pushed the feat/auto-create-prerequisites branch from d7cb3fc to 9c3cbfe Compare June 4, 2026 04:09
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. label Jun 4, 2026
@matzew

matzew commented Jun 9, 2026

Copy link
Copy Markdown
Member

ServiceAccount lookups should use APIReader instead of the cached client

The prerequisites code uses r.Get() (cached client) for ServiceAccount existence checks. This forces controller-runtime to spin up a ServiceAccount informer (initial
LIST + persistent WATCH of all ServiceAccounts in memory), even though SA lookups only happen on the infrequent, opt-in annotation path.

This is the same problem PR #204 fixed for pods. That PR switched r.List() to r.APIReader.List() specifically to avoid creating a heavyweight pod informer for an
infrequent diagnostic path. The same principle applies here: don't use the cached client for resource types the controller doesn't already watch.

Note that ConfigMaps are fine with r.Get() because the controller already watches ConfigMaps in SetupWithManager - that informer is already running. But
ServiceAccounts have no watch registered, so the cached client would create an entirely new informer just for this opt-in path.

Fix: Use r.APIReader.Get() for the ServiceAccount existence check and drop watch from the ServiceAccount RBAC marker:

// prerequisites code:
err := r.APIReader.Get(ctx, client.ObjectKey{Name: saName, Namespace: server.Namespace}, sa)

// RBAC marker:
// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;create

https://github.com/kubernetes-sigs/mcp-lifecycle-operator/blob/9c3cbfed38dcf7e30a5a03e9b115e57eaabb2deb/internal/controller/mcpserver_controller_prerequisites.go#L72-L80

Reference: PR #204 established this pattern - #204

@matzew

matzew commented Jun 9, 2026

Copy link
Copy Markdown
Member

Error swallowing in ensurePrerequisites - incorrect assumption for ServiceAccounts

The error from ensurePrerequisites() is intentionally swallowed:

if err := r.ensurePrerequisites(ctx, mcpServer); err != nil {
    logger.Error(err, "Failed to create prerequisites")
    // Don't return error — fall through to validation which will report the specific missing resource
}

The comment says "fall through to validation which will report the specific missing resource". This assumption holds for ConfigMaps (validated by validateStorageMount),
but validateConfig does not validate ServiceAccount existence at all - it only covers storage mounts, envFrom, and env valueFrom references.

Failure scenario:

  1. User creates MCPServer with auto-create-prerequisites: "true" and a serviceAccountName
  2. ensureServiceAccount() fails with a transient error (API timeout, 500, etc.)
  3. Error is logged but swallowed, reconcile continues
  4. validateConfig() passes - it doesn't check ServiceAccount existence
  5. Deployment is created referencing a ServiceAccount that doesn't exist
  6. Pods fail to schedule/start
  7. Next reconcile: same transient error, same swallow, same result

The transient error never triggers a retry because it's never returned. The controller's established pattern (see classifyAPIError) returns transient errors so the
reconcile loop retries with backoff.

Fix options:

  • Return the error from ensurePrerequisites so transient failures are retried, or
  • Add ServiceAccount validation to validateConfig so the assumption in the comment actually holds

https://github.com/kubernetes-sigs/mcp-lifecycle-operator/blob/9c3cbfed38dcf7e30a5a03e9b115e57eaabb2deb/internal/controller/mcpserver_controller.go#L199-L203

@matzew

matzew commented Jun 9, 2026

Copy link
Copy Markdown
Member

Thinking about it - why should the operator create resources for 3rd party functionality

This is however to me a big diff to what we create for "running" (or operating) the mcp server (e.g. deployment / service)

Perhaps something on that "catalog" should do more - as an integrating factor?

Use r.APIReader.Get() instead of r.Get() for ServiceAccount existence
checks to avoid spinning up an unnecessary informer on the opt-in
annotation path. Follows the pattern established in PR kubernetes-sigs#204.

Return errors from ensurePrerequisites() so transient failures trigger
reconcile retries with backoff, rather than swallowing them and
proceeding to create a Deployment referencing a potentially missing SA.

Drop list/watch from ServiceAccount RBAC since the controller doesn't
need an informer for this resource type.

Assisted-by: Claude Code (Opus 4.6)
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds auto-create-prerequisites functionality to the MCPServer controller. When the mcp.x-k8s.io/auto-create-prerequisites annotation is set to "true", the reconciler automatically creates missing ServiceAccount and ConfigMap prerequisites before validation. RBAC permissions, helper logic, integration, and comprehensive test coverage are included.

Changes

Auto-Create Prerequisites Feature

Layer / File(s) Summary
RBAC Permissions & Annotations
config/rbac/role.yaml, internal/controller/mcpserver_controller.go
ClusterRole manager-role is extended with create verb for configmaps and serviceaccounts, and get/list/watch for secrets. Kubebuilder RBAC annotations on MCPServerReconciler are updated to reflect these permissions.
Prerequisite Auto-Creation Implementation
internal/controller/mcpserver_controller_prerequisites.go
New helper file implements ensurePrerequisites orchestrator, ensureServiceAccount to read and create missing ServiceAccounts with controller owner reference, ensureConfigMaps to scan storage entries and create missing ConfigMap-backed prerequisites with ownership, and emitPrerequisiteCreated to record Kubernetes events.
Controller Reconciliation Integration
internal/controller/mcpserver_controller.go
Reconcile method invokes ensurePrerequisites early in the loop; errors are logged and returned to trigger backoff retry.
Test Validation
internal/controller/mcpserver_controller_prerequisites_test.go
Ginkgo test suite covers annotation presence/value, auto-creation of missing prerequisites with proper ownership, preservation of existing resources, event emission, storage type filtering, and failure scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A prerequisite hop, so smooth and neat,
ServiceAccounts and ConfigMaps—auto-create, no repeat!
When annotations call, the controller heeds,
With RBAC blessed to fulfill these needs.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(controller): create prerequisites from annotation' clearly and specifically summarizes the main change: adding functionality to auto-create Kubernetes prerequisites (ServiceAccounts and ConfigMaps) based on an annotation, which is the core feature across all modified and new files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

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

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

@rdwj

rdwj commented Jun 10, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough review @matzew — both catches are spot on.

APIReader for ServiceAccount lookups: Great catch — I should have followed the pattern from #204. Switched to r.APIReader.Get() and dropped list/watch from the SA RBAC marker since we don't need an informer for this opt-in path.

Error swallowing: You're right that the "fall through to validation" assumption doesn't hold for ServiceAccounts since validateConfig only covers storage mounts and env refs. Changed to return the error so transient failures get retried with backoff, which is consistent with how classifyAPIError works elsewhere in the controller.

Both fixes are in 6aef031. All tests and lint pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/controller/mcpserver_controller_prerequisites_test.go (1)

132-371: ⚡ Quick win

Add one regression test for prerequisite API-error propagation.

Current coverage validates happy paths/gating well, but it does not assert that a failure inside prerequisite creation is returned from Reconcile (so controller-runtime retries). Add a case that injects a failing read/create for prerequisite resources and expects a non-nil reconcile error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/mcpserver_controller_prerequisites_test.go` around lines
132 - 371, Add a regression test that injects a failing client behavior and
asserts Reconcile returns an error: create a new It case (e.g. "should propagate
API errors when creating prerequisites") that builds an MCPServer with
AnnotationAutoCreatePrerequisites="true" and prerequisite names
(ServiceAccount/ConfigMap), then construct the reconciler using the test helper
that accepts a client wrapper (use or add a failing client/fake that returns an
error from Get/Create for resources used in prerequisite creation), call
controllerReconciler.Reconcile(ctx, reconcile.Request{NamespacedName:
typeNamespacedName}) and Expect(err).To(HaveOccurred()) to verify the error is
propagated; reference helpers/newReconcilerForTest (or
newReconcilerForTestWithFakeEvents) and the Reconcile method to locate where to
inject the failing client and the assertion.
🤖 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.

Nitpick comments:
In `@internal/controller/mcpserver_controller_prerequisites_test.go`:
- Around line 132-371: Add a regression test that injects a failing client
behavior and asserts Reconcile returns an error: create a new It case (e.g.
"should propagate API errors when creating prerequisites") that builds an
MCPServer with AnnotationAutoCreatePrerequisites="true" and prerequisite names
(ServiceAccount/ConfigMap), then construct the reconciler using the test helper
that accepts a client wrapper (use or add a failing client/fake that returns an
error from Get/Create for resources used in prerequisite creation), call
controllerReconciler.Reconcile(ctx, reconcile.Request{NamespacedName:
typeNamespacedName}) and Expect(err).To(HaveOccurred()) to verify the error is
propagated; reference helpers/newReconcilerForTest (or
newReconcilerForTestWithFakeEvents) and the Reconcile method to locate where to
inject the failing client and the assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ec28f6c-bc20-4942-9bde-48f7065e2e7e

📥 Commits

Reviewing files that changed from the base of the PR and between b7521b0 and 6aef031.

📒 Files selected for processing (4)
  • config/rbac/role.yaml
  • internal/controller/mcpserver_controller.go
  • internal/controller/mcpserver_controller_prerequisites.go
  • internal/controller/mcpserver_controller_prerequisites_test.go

@matzew

matzew commented Jun 10, 2026

Copy link
Copy Markdown
Member

I am not sure we really must have this knob?

See: #227 (comment)

@ArangoGutierrez ArangoGutierrez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Useful feature and a clean implementation — the annotation matches the reserved mcp.x-k8s.io/ prefix, events follow the house style, and the envtest cases assert real behavior (including the existing-resources-untouched case).

Requesting changes for three things:

  • SA auto-creation needs a security story: it can materialize a principal that pre-existing RoleBindings already grant permissions to (details inline).
  • The annotation is user-facing but undocumented — site-src deserves a section; the introduction.md labels/annotations area is a natural home.
  • The empty-ConfigMap-plus-owner-reference semantics should be documented or reconsidered (inline).

Nit: the PR body says serviceaccounts gained list/watch/create, but the diff adds get/create — worth syncing the description.

// ensureServiceAccount creates the ServiceAccount referenced by
// spec.runtime.security.serviceAccountName if it does not already exist.
func (r *MCPServerReconciler) ensureServiceAccount(ctx context.Context, server *mcpv1alpha1.MCPServer) error {
saName := server.Spec.Runtime.Security.ServiceAccountName

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Auto-creating SAs lends the operator's create serviceaccounts privilege to anyone who can create an MCPServer. The sharp edge: if a RoleBinding or ClusterRoleBinding already exists granting permissions to a not-yet-existing SA name in the namespace, creating an MCPServer with that serviceAccountName materializes the principal and runs the workload under those permissions — a capability the MCPServer author may not otherwise have. Worth covering in the docs/threat model, and consider limiting auto-creation to a convention-derived name (e.g. <mcpserver-name>-sa) rather than any spec-supplied value.

}

sa := &corev1.ServiceAccount{}
err := r.APIReader.Get(ctx, client.ObjectKey{Name: saName, Namespace: server.Namespace}, sa)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good call using APIReader here — with only get;create RBAC on serviceaccounts, a cached r.Get would try to start an SA informer and fail at runtime. Worth a short code comment saying so, so a future refactor doesn't simplify this back to r.Get.

Name: cmName,
Namespace: server.Namespace,
},
Data: map[string]string{},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two questions on the empty-ConfigMap semantics: (1) the server now passes validation and starts with an empty config mount — is that useful on its own for the catalog flow, or does something populate the CM afterwards? (2) If something does populate it later, the owner reference means deleting the MCPServer garbage-collects the populated CM, and a recreate starts from empty again. If the CM is meant to outlive the CR once it carries real data, owning it may be the wrong default; at minimum the behavior should be documented with the annotation.

// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;create

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deletion drift: ConfigMaps are watched (findMCPServersForConfigMap), so removing an auto-created CM re-triggers reconcile and it comes back. ServiceAccounts have no watch, so a deleted auto-created SA stays missing until an unrelated event hits the MCPServer. If you want symmetric self-healing, Owns(&corev1.ServiceAccount{}) plus list;watch verbs would do it — otherwise fine to leave, but the asymmetry is worth a comment.

@rdwj

rdwj commented Jun 12, 2026

Copy link
Copy Markdown
Author

@matzew — fair point, and after running this in practice I've come around to agreeing with you.

When we first hit this with the RHOAI catalog, the manual SA/CM setup felt like a sharp edge worth smoothing. But we've since run the workshop roughly 10 times with proper documentation, and the prerequisite creation hasn't been a real blocker. The operator probably shouldn't be in the business of materializing resources for third-party functionality — that's a catalog/packaging concern.

That said, @ArangoGutierrez left a thorough review with valid points (security implications of SA auto-creation, ownership semantics, watch asymmetry). I plan to address those so the diff reflects sound design in case the feature is ever reconsidered. But I'm equally comfortable closing this PR if you'd prefer.

Let me know how you'd like to proceed — happy to withdraw or iterate.

@rdwj

rdwj commented Jun 12, 2026

Copy link
Copy Markdown
Author

@ArangoGutierrez — thank you for the detailed review. Addressing each item:

SA security / naming: You're right that auto-creating a named SA can silently activate pre-existing RoleBindings. A convention-derived name like <mcpserver-name>-sa is cleaner and makes the blast radius explicit. Agreed.

APIReader comment: Will add a comment explaining the uncached read rationale so it survives future refactors.

ConfigMap ownership semantics: These are intended as structural placeholders — in the catalog flow, actual configuration is injected separately. The owner reference for GC is intentional, but I agree this needs clear documentation so users don't lose data unexpectedly.

SA watch asymmetry: Good catch. At minimum I'll add a comment noting the gap. A full SA watch can be a follow-up if the feature moves forward.

I'll also fix the PR body (list/watch/create should be get/create).

One note: @matzew raised a broader question about whether this feature belongs in the operator at all. After reflection, I tend to agree — documentation has been sufficient in practice. If the maintainers prefer to close this, I'm comfortable withdrawing.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 17, 2026
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@ArangoGutierrez ArangoGutierrez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Clean, well-scoped implementation: the annotation uses the reserved prefix, resources are owned and created idempotently, prior review feedback on APIReader and error propagation is addressed, and most envtest cases assert real behavior. Holding at request-changes for one make-or-break item plus two supporting ones. The security concern raised earlier is unaddressed: auto-creating a ServiceAccount from a spec-supplied name lets an MCPServer author activate a dormant RoleBinding and run under privileges they don't otherwise hold (no webhook constrains the name); please constrain to a CR-derived name and document the threat model. The head commit's error-propagation fix has no test pinning it — a swallowed error would go unnoticed, so please add the regression case CodeRabbit suggested. Finally the branch needs a rebase: there's a real content conflict in mcpserver_controller.go against main's NetworkPolicy work (role.yaml auto-merges). build/vet/test are all green on the head as-is.

// ensureServiceAccount creates the ServiceAccount referenced by
// spec.runtime.security.serviceAccountName if it does not already exist.
func (r *MCPServerReconciler) ensureServiceAccount(ctx context.Context, server *mcpv1alpha1.MCPServer) error {
saName := server.Spec.Runtime.Security.ServiceAccountName

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Security: this auto-creates a SA whose name comes straight from spec.runtime.security.serviceAccountName. Because the operator holds cluster-wide create-serviceaccounts, an MCPServer author who can't create SAs directly can name a SA that a pre-existing RoleBinding already privileges, and the workload then runs with those rights (confined to this namespace). Consider deriving the name from the CR (-sa) or restricting to that convention, and document the threat model.

}

sa := &corev1.ServiceAccount{}
err := r.APIReader.Get(ctx, client.ObjectKey{Name: saName, Namespace: server.Namespace}, sa)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Worth a short comment that APIReader is intentional here: SA RBAC is get;create with no watch, so a cached r.Get would try to start an SA informer and fail at runtime. Keeps a future refactor from simplifying it back to r.Get.

Name: cmName,
Namespace: server.Namespace,
},
Data: map[string]string{},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The auto-created ConfigMap is empty and owned by the CR. If something populates it later, deleting the MCPServer will GC the populated data and a recreate starts empty. If the CM is meant to outlive the CR once it holds real data, owning it may be the wrong default — at least document this with the annotation.

NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This case only asserts err == nil, so it doesn't actually verify the skip. Add asserts that no ConfigMap was created and no CreatedPrerequisite event fired for the emptyDir entry, otherwise a bug that created a CM here would pass.

}
})

It("should create a missing ServiceAccount", func() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please add a case that injects a failing Get/Create and asserts Reconcile returns a non-nil error. The head commit's whole point is returning prerequisite errors so controller-runtime retries, but nothing pins that today — reverting the return to a swallow would keep the suite green.

// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;create

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deletion asymmetry: watched ConfigMaps self-heal if deleted, but an auto-created SA won't (no watch/Owns) until an unrelated event hits the MCPServer. Fine to leave given the APIReader-avoids-informer choice, but worth a one-line comment noting the asymmetry is intentional.

@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants