Align imagePullSecrets merge semantics across MCPRegistry and VirtualMCPServer
Nobody has claimed this yet.
- 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-generatedPodSpec.ImagePullSecrets(fromspec.imagePullSecrets)
is strategic-merge-patched against the user'sspec.podTemplateSpec.spec.imagePullSecrets,
keyed byName. Distinct names are unioned; same names dedup with the
PodTemplateSpec entry winning on overlap. This is whatcorev1.PodSpec
patchStrategy:\"merge\"/patchMergeKey:\"name\"tags prescribe. -
MCPRegistry (#5106): atomic replace via the custom
MergePodTemplateSpecs
helper incmd/thv-operator/pkg/registryapi/podtemplatespec.go. If the user
setsspec.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:
-
Align both CRDs to Kubernetes-native strategic merge by name. vMCP already
does this. MCPRegistry'sMergePodTemplateSpecswould be replaced with a raw
strategic-merge-patch path (the same approachVirtualMCPServerand
EmbeddingServeruse, 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. -
Align both CRDs to atomic replace. vMCP would adopt MCPRegistry's
replace-wholesale behavior forimagePullSecrets. 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
ImagePullSecretsfield on
VirtualMCPServerSpecincmd/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
ImagePullSecretsgodoc - Tests cover the chosen behavior on both CRDs
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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