Skip to content

Remove deprecated legacy typed fields from MCPRegistry CRD#4705

Merged
ChrisJBurns merged 5 commits intomainfrom
worktree-registry-crd-refactor
Apr 10, 2026
Merged

Remove deprecated legacy typed fields from MCPRegistry CRD#4705
ChrisJBurns merged 5 commits intomainfrom
worktree-registry-crd-refactor

Conversation

@ChrisJBurns
Copy link
Copy Markdown
Collaborator

@ChrisJBurns ChrisJBurns commented Apr 9, 2026

Summary

Removes all legacy typed config fields from the MCPRegistry CRD after the decoupled configYAML path was added in #4693. The configYAML field is now the only way to configure the registry server, eliminating ~10,900 net lines of coupled config transformation code.

This is a breaking CRD change — existing MCPRegistry resources using the legacy typed fields (sources, registries, databaseConfig, authConfig, telemetryConfig) must be migrated to configYAML before upgrading the CRD.

Closes #4704

Large PR Justification

This PR removes the legacy typed config path from the MCPRegistry CRD and operator. The changes span CRD types, config generation, controller logic, deployment builders, PodTemplateSpec options, tests, and examples — all tightly coupled to the same legacy types. Splitting would leave the codebase in a non-compiling intermediate state since deleting a CRD type breaks every file that references it simultaneously.

  • ~10,900 net lines removed (legacy code + tests + examples)
  • Remaining ~665 lines are integration test rewrites and field comment updates
  • All changes are mechanical removal or test fixture migration

Migration Guide

Users must migrate existing MCPRegistry resources before upgrading to this CRD version.

Before (legacy typed path)

apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPRegistry
metadata:
  name: my-registry
spec:
  sources:
    - name: production
      format: toolhive
      configMapRef:
        name: prod-registry
        key: registry.json
      syncPolicy:
        interval: "1h"
  registries:
    - name: default
      sources: ["production"]
  databaseConfig:
    host: postgres
    port: 5432
    user: db_app
    migrationUser: db_migrator
    database: registry
    sslMode: require
    dbAppUserPasswordSecretRef:
      name: db-credentials
      key: app_password
    dbMigrationUserPasswordSecretRef:
      name: db-credentials
      key: migration_password
  authConfig:
    mode: anonymous

After (configYAML path)

apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPRegistry
metadata:
  name: my-registry
spec:
  configYAML: |
    sources:
      - name: production
        format: toolhive
        file:
          path: /config/registry/production/registry.json
        syncPolicy:
          interval: 1h
    registries:
      - name: default
        sources: ["production"]
    database:
      host: postgres
      port: 5432
      user: db_app
      migrationUser: db_migrator
      database: registry
      sslMode: require
    auth:
      mode: anonymous
  volumes:
    - name: registry-data
      configMap:
        name: prod-registry
        items:
          - key: registry.json
            path: registry.json
  volumeMounts:
    - name: registry-data
      mountPath: /config/registry/production
      readOnly: true
  pgpassSecretRef:
    name: my-pgpass-secret
    key: .pgpass

Migration steps

  1. Write your config as YAML — take the registry server config that was previously generated by the operator and put it directly in spec.configYAML
  2. Move volume sources — ConfigMap/PVC/Secret references move from typed fields to spec.volumes and spec.volumeMounts with explicit mount paths matching the file paths in configYAML
  3. Create pgpass Secret — if using databaseConfig, create a pgpass-formatted Secret and reference it via spec.pgpassSecretRef (the operator handles the init container and chmod 0600 automatically)
  4. Do not inline credentialsconfigYAML is stored in a ConfigMap, not a Secret. Reference credentials via file paths and mount actual secrets using volumes/volumeMounts
  5. Remove legacy fields — delete spec.sources, spec.registries, spec.databaseConfig, spec.authConfig, spec.telemetryConfig

See examples/operator/mcp-registries/mcpregistry-configyaml-*.yaml for complete examples covering all source types.

Type of change

  • Breaking change

Test plan

  • Unit tests (task test) — all passing
  • Linting (task lint) — 0 issues
  • Build verification (go build ./cmd/thv-operator/...)
  • CRD manifest regeneration (task operator-generate && task operator-manifests)
  • CRD API docs regeneration (task crdref-gen)

Changes

File Change
mcpregistry_types.go Remove 25+ legacy types, 5 deprecated fields, 4 helper methods; make configYAML required; remove CEL rules; add security guidance
zz_generated.deepcopy.go Regenerated (much smaller)
config/config.go Stripped to 2 constants — all ConfigManager, build*, and config struct types deleted (~1,100 lines)
config/config_test.go Deleted entirely (~2,170 lines)
pgpass.go + pgpass_test.go Deleted entirely (~420 lines)
deployment.go Delete legacy builder; rename NewPath → primary functions
manager.go Delete reconcileLegacyPath; inline new path into ReconcileAPIService
podtemplatespec.go Delete WithRegistrySourceMounts, WithGitAuthMount, WithPGPassMount, WithRegistryStorageMount
types.go Delete legacy constants
mcpregistry_controller.go Delete validatePathExclusivity; simplify validateSpec
Controller + registryapi tests Delete legacy tests; update fixtures to use configYAML
Integration tests Rewrite RegistryBuilder to generate configYAML strings
14 legacy example YAMLs Deleted
CRD manifests Regenerated
docs/operator/crd-api.md Regenerated

Special notes for reviewers

  • Net diff: -10,900 lines — most is deleted legacy code and examples
  • GetStorageName() is kept — still used by pkg/validation/image_validation.go. Filed Fix image_validation.go GetStorageName() for configYAML path #4717 to investigate and fix the latent bug where enforceServers no longer works with the configYAML path.
  • Integration test RegistryBuilder was completely rewritten to construct configYAML YAML strings instead of typed Go structs
  • Config validation (SSL modes, OAuth audience, source type exclusivity) is now the registry server's responsibility — the operator passes through raw YAML without parsing

Generated with Claude Code

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Large PR Detected

This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.

How to unblock this PR:

Add a section to your PR description with the following format:

## Large PR Justification

[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformation

Alternative:

Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.

See our Contributing Guidelines for more details.


This review will be automatically dismissed once you add the justification section.

@github-actions github-actions bot added the size/XL Extra large PR: 1000+ lines changed label Apr 9, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 68.82%. Comparing base (1e6d778) to head (531b0af).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
...thv-operator/controllers/mcpregistry_controller.go 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4705      +/-   ##
==========================================
+ Coverage   68.59%   68.82%   +0.22%     
==========================================
  Files         517      515       -2     
  Lines       54631    54115     -516     
==========================================
- Hits        37474    37242     -232     
+ Misses      14268    14004     -264     
+ Partials     2889     2869      -20     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
Remove all legacy typed config fields, config generation code, and
associated tests after the decoupled configYAML path was added in
#4693. The configYAML field is now the only way to configure the
registry server.

CRD changes:
- Remove Sources, Registries, DatabaseConfig, AuthConfig,
  TelemetryConfig fields and all 25+ associated types
- Make configYAML required
- Remove CEL validation rules (configYAML is required via markers)
- Delete legacy helper methods (HasDatabaseConfig, GetDatabaseConfig,
  GetDatabasePort, BuildPGPassSecretName)

Operator code removal:
- Delete config/config.go legacy ConfigManager and all build* functions
  (~1,100 lines)
- Delete config/config_test.go (~2,170 lines)
- Delete pgpass.go and pgpass_test.go (~420 lines)
- Delete WithRegistrySourceMounts, WithGitAuthMount, WithPGPassMount,
  WithRegistryStorageMount from podtemplatespec.go
- Delete reconcileLegacyPath, ensurePGPassSecret from manager.go
- Delete legacy buildRegistryAPIDeployment and ensureDeployment
- Rename NewPath variants to be the primary functions
- Simplify controller validation (remove mutual exclusivity checks)

Tests and examples:
- Rewrite integration test RegistryBuilder to generate configYAML
- Delete all legacy unit tests
- Delete 14 legacy example YAML files
- Regenerate CRD manifests and API docs

Closes #4704

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ChrisJBurns ChrisJBurns force-pushed the worktree-registry-crd-refactor branch from d3ed1e0 to f430124 Compare April 9, 2026 20:09
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
Copy link
Copy Markdown
Collaborator Author

@ChrisJBurns ChrisJBurns left a comment

Choose a reason for hiding this comment

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

Multi-Agent Consensus Review

Agents consulted: kubernetes-expert, code-reviewer, toolhive-expert, security-advisor

Consensus Summary

# Finding Consensus Severity Action
1 Migration documentation needed for breaking CRD change 9/10 MEDIUM Document
2 GetStorageName() / image_validation.go latent bug 7/10 MEDIUM Investigate
3 Loss of operator-enforced security invariants 8/10 MEDIUM Document
4 configYAML in ConfigMap could contain inline credentials 7/10 LOW Document
5 Test helper builds YAML via string concatenation 7/10 LOW Nit
6 config package doc misleading after cleanup 7/10 LOW Fix

Finding #2 (non-inline): GetStorageName() / image_validation.go latent bug cannot be mapped to this diff because image_validation.go is not part of the changed files. The issue: image_validation.go:206 uses GetStorageName() to look up a {name}-registry-storage ConfigMap that the legacy path created. The new configYAML path creates {name}-registry-server-config instead. With the legacy path removed, image enforcement via enforceServers will silently degrade (ConfigMap not found -> returns false). This pre-dates the PR but is now the only path. Consider filing a tracking issue.

Overall

This is a clean, well-executed removal of ~10,900 lines of legacy typed-config code from the MCPRegistry CRD. The deletion is thorough — all four agents confirmed no orphaned references to removed types, functions, or constants in production code. The integration tests are properly rewritten to use the configYAML approach with explicit volume/mount construction, maintaining equivalent coverage.

The findings are documentation and follow-up items rather than code defects. The most significant architectural concern is the validation shift: moving from typed CRD fields with CEL rules and kubebuilder validation to raw configYAML means the operator no longer enforces security-relevant configuration (auth mode, SSL mode, audience) at admission time. This is an accepted tradeoff of the "operator does NOT parse, validate, or transform" design, but should be documented clearly for users. The PGPass handling is actually a security improvement — the operator no longer reads raw database credentials during reconciliation.


Generated with Claude Code

@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
@github-actions github-actions bot dismissed their stale review April 9, 2026 21:04

Large PR justification has been provided. Thank you!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

✅ Large PR justification has been provided. The size review has been dismissed and this PR can now proceed with normal review.

@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
Add security note to configYAML field warning against inlining
credentials — configYAML is stored in a ConfigMap, not a Secret.
Recommend using file paths with mounted Secrets via volumes.

Fix stale package doc on config package after legacy code removal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ChrisJBurns ChrisJBurns force-pushed the worktree-registry-crd-refactor branch from 3a0b3f3 to ed3544a Compare April 9, 2026 21:05
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
@ChrisJBurns ChrisJBurns marked this pull request as ready for review April 9, 2026 21:25
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
Rewrite all MCPRegistry documentation to reflect the removal of legacy
typed fields. The configYAML pass-through path is now the only way to
configure the registry server.

REGISTRY.md: Rewrite all YAML examples (ConfigMap, Git, API, OAuth,
filtering, production/dev/multi-source) to use configYAML with
volumes/volumeMounts. Add CRD fields reference table, config YAML
structure guide, database/pgpass section, and security guidance about
not inlining credentials.

06-registry-system.md: Update architecture examples from old typed
source/configMapRef patterns to configYAML blocks. Explain the
pass-through model.

README.md: Replace legacy MCPRegistry example with configYAML version.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ChrisJBurns ChrisJBurns force-pushed the worktree-registry-crd-refactor branch from 77ce2cd to 4972d27 Compare April 9, 2026 21:36
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
The registry server uses in-memory git clones and PostgreSQL for all
storage — the emptyDir at /data was removed in the Phase 2 cleanup.
The integration test still asserted its presence.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ChrisJBurns ChrisJBurns force-pushed the worktree-registry-crd-refactor branch from 4d8b3c1 to 531b0af Compare April 9, 2026 22:41
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 9, 2026
@ChrisJBurns ChrisJBurns merged commit ecd7872 into main Apr 10, 2026
40 checks passed
@ChrisJBurns ChrisJBurns deleted the worktree-registry-crd-refactor branch April 10, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Extra large PR: 1000+ lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove deprecated legacy typed fields from MCPRegistry CRD

2 participants