stacklok / stacklok/toolhive

Align imagePullSecrets merge semantics across MCPRegistry and VirtualMCPServer

Open
#5,111 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement kubernetes operator tech-debt vmcp
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Summary

MCPRegistry and VirtualMCPServer use different merge strategies when reconciling
spec.imagePullSecrets against the user-provided spec.podTemplateSpec. A user
managing both CRDs gets different semantics for what looks like the same field.

Divergence

  • VirtualMCPServer (#5107): Kubernetes-native strategic merge by name. The
    controller-generated PodSpec.ImagePullSecrets (from spec.imagePullSecrets)
    is strategic-merge-patched against the user's spec.podTemplateSpec.spec.imagePullSecrets,
    keyed by Name. Distinct names are unioned; same names dedup with the
    PodTemplateSpec entry winning on overlap. This is what corev1.PodSpec
    patchStrategy:\"merge\" / patchMergeKey:\"name\" tags prescribe.

  • MCPRegistry (#5106): atomic replace via the custom MergePodTemplateSpecs
    helper in cmd/thv-operator/pkg/registryapi/podtemplatespec.go. If the user
    sets spec.podTemplateSpec.spec.imagePullSecrets, the controller-generated
    list is replaced wholesale rather than merged.

The same manifest layout produces different deployed PodSpec.ImagePullSecrets
values depending on which CRD applies it. This is surprising for users who
expect K8s-native behavior on both, and it makes shared documentation impossible.

Resolutions to consider

Two reasonable options — both should be evaluated and only one chosen:

  1. Align both CRDs to Kubernetes-native strategic merge by name. vMCP already
    does this. MCPRegistry's MergePodTemplateSpecs would be replaced with a raw
    strategic-merge-patch path (the same approach VirtualMCPServer and
    EmbeddingServer use, see #5104). Pros: consistent with the rest of the
    ecosystem; least surprising for K8s users. Cons: changes existing MCPRegistry
    semantics — users relying on atomic-replace would observe a behavior change.

  2. Align both CRDs to atomic replace. vMCP would adopt MCPRegistry's
    replace-wholesale behavior for imagePullSecrets. Pros: predictable single
    source of truth. Cons: diverges from K8s-native PodSpec behavior, requires a
    custom merge step that has to be kept in sync with future CRD additions, and
    contradicts the godoc that just landed in #5107.

This issue is not asking the implementer to pick a side — it's asking the
maintainers to decide so we can converge.

Context

  • vMCP behavior: stacklok/toolhive#5107
  • MCPRegistry context: stacklok/toolhive#5106
  • vMCP godoc that documents the divergence: see the ImagePullSecrets field on
    VirtualMCPServerSpec in cmd/thv-operator/api/v1beta1/virtualmcpserver_types.go
    ("Note on cross-CRD consistency").

Acceptance criteria

  • Decision recorded on which strategy both CRDs will use
  • One CRD's behavior is updated to match the other (with a release note if
    it is a behavior change)
  • The "Note on cross-CRD consistency" paragraph is removed from the vMCP
    ImagePullSecrets godoc
  • Tests cover the chosen behavior on both CRDs

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by comparing MergePodTemplateSpecs in cmd/thv-operator/pkg/registryapi/podtemplatespec.go with the strategic-merge paths used by VirtualMCPServer and EmbeddingServer, and read the ImagePullSecrets godoc in cmd/thv-operator/api/v1beta1/virtualmcpserver_types.go. First get a maintainer decision on the strategy, then inspect both CRD reconciliation tests. Done means both CRDs share the chosen behavior, tests cover it, and the cross-CRD note is removed with any required release note.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, kubernetes
Domain
backend, infrastructure
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.