Summary
MCPRegistry uses corev1.SecretKeySelector for 5 secret reference fields while every other CRD (MCPServer, MCPOIDCConfig, MCPExternalAuthConfig, MCPRemoteProxy, MCPTelemetryConfig) uses a custom SecretKeyRef type. This should be standardized before the API stabilizes.
The custom SecretKeyRef is the right target because:
- Already used by 5 of 6 CRDs (13+ fields vs 5)
- Simpler — just
Name + Key, both required, no unused Optional field
corev1.SecretKeySelector exposes an Optional *bool field that no controller respects — the secrets.GetValue() function always fails if the secret is missing regardless of that flag
Custom SecretKeyRef definition
Already defined in cmd/thv-operator/api/v1alpha1/mcpexternalauthconfig_types.go (lines 589-598):
type SecretKeyRef struct {
// Name is the name of the secret
// +kubebuilder:validation:Required
Name string `json:"name"`
// Key is the key within the secret
// +kubebuilder:validation:Required
Key string `json:"key"`
}
What to change
1. CRD type definitions (cmd/thv-operator/api/v1alpha1/mcpregistry_types.go)
Convert these 5 fields from corev1.SecretKeySelector to the custom SecretKeyRef type (from mcpexternalauthconfig_types.go). Since all types are in the same package, no import is needed.
| Line |
Struct |
Field |
Current type |
New type |
| ~181 |
GitAuthConfig |
PasswordSecretRef |
corev1.SecretKeySelector |
SecretKeyRef |
| ~347 |
MCPRegistryDatabaseConfig |
DBAppUserPasswordSecretRef |
corev1.SecretKeySelector |
SecretKeyRef |
| ~354 |
MCPRegistryDatabaseConfig |
DBMigrationUserPasswordSecretRef |
corev1.SecretKeySelector |
SecretKeyRef |
| ~444 |
MCPRegistryOAuthProviderConfig |
ClientSecretRef |
*corev1.SecretKeySelector |
*SecretKeyRef |
| ~461 |
MCPRegistryOAuthProviderConfig |
AuthTokenRef |
*corev1.SecretKeySelector |
*SecretKeyRef |
Note: preserve pointer vs value semantics — ClientSecretRef and AuthTokenRef are pointers (optional), the others are values (required).
Important: Do NOT remove the corev1 import from this file — it's still used for corev1.ConfigMapKeySelector (lines ~86, ~450) and corev1.LocalObjectReference (line ~625).
2. Secrets client (cmd/thv-operator/pkg/kubernetes/secrets/secrets.go)
Change the GetValue method signature at line 50 from:
func (c *Client) GetValue(ctx context.Context, namespace string, secretRef corev1.SecretKeySelector) (string, error) {
to accept name and key strings directly:
func (c *Client) GetValue(ctx context.Context, namespace, name, key string) (string, error) {
Update the body (lines 51, 56, 58) to use name and key instead of secretRef.Name and secretRef.Key. This decouples the shared utility from any specific ref type.
3. Callers of GetValue (cmd/thv-operator/pkg/registryapi/pgpass.go)
Only 2 call sites (lines ~36 and ~43). Update from:
m.kubeHelper.Secrets.GetValue(ctx, mcpRegistry.Namespace, dbConfig.DBAppUserPasswordSecretRef)
to:
m.kubeHelper.Secrets.GetValue(ctx, mcpRegistry.Namespace, dbConfig.DBAppUserPasswordSecretRef.Name, dbConfig.DBAppUserPasswordSecretRef.Key)
Same for DBMigrationUserPasswordSecretRef.
4. Config builder (cmd/thv-operator/pkg/registryapi/config/config.go)
Two helper functions accept *corev1.SecretKeySelector — change them to *SecretKeyRef:
buildSecretFilePath (line ~764): change parameter from *corev1.SecretKeySelector to *mcpv1alpha1.SecretKeyRef (or just *SecretKeyRef if in same package). The body accesses .Name and .Key which exist on both types — no logic changes needed.
buildGitPasswordFilePath (line ~549): same change.
Also update field access at lines ~533, ~537 in buildGitAuthConfig — these access auth.PasswordSecretRef.Name and .Key which work identically on the custom type.
5. PodTemplateSpec builder (cmd/thv-operator/pkg/registryapi/podtemplatespec.go)
WithGitAuthMount function (line ~350): change parameter from corev1.SecretKeySelector to the custom type. The body accesses .Name and .Key — no logic changes needed.
6. Test files
Update all test fixtures that construct corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "..."}, Key: "..."} to the simpler SecretKeyRef{Name: "...", Key: "..."}:
cmd/thv-operator/pkg/registryapi/pgpass_test.go (~lines 259-265)
cmd/thv-operator/pkg/registryapi/config/config_test.go (~lines 498, 546, 584, 622, 1676, 2179, 2231)
cmd/thv-operator/pkg/kubernetes/secrets/secrets_test.go (~lines 143, 166, 201, 246, 280) — update to pass name, key strings instead of SecretKeySelector
7. Regenerate
After all changes:
task gen # Regenerates deepcopy and CRD manifests
task crdref-gen # Regenerates CRD API docs
task lint-fix # Fix any lint issues
task lint # Verify clean
task test # Unit tests pass
The deepcopy code in zz_generated.deepcopy.go will regenerate automatically — do not edit it manually.
Notes
- The JSON wire format is identical for both types (
{"name": "...", "key": "..."}) so existing manifests continue to work without changes.
- The
Optional field from corev1.SecretKeySelector was never checked by any controller — removing it has no behavioral impact.
- The custom
SecretKeyRef type is defined in mcpexternalauthconfig_types.go but is used across multiple CRDs. Consider whether it should be moved to a shared types file in the future, but that's out of scope for this issue.
Generated with Claude Code
Summary
MCPRegistry uses
corev1.SecretKeySelectorfor 5 secret reference fields while every other CRD (MCPServer, MCPOIDCConfig, MCPExternalAuthConfig, MCPRemoteProxy, MCPTelemetryConfig) uses a customSecretKeyReftype. This should be standardized before the API stabilizes.The custom
SecretKeyRefis the right target because:Name+Key, both required, no unusedOptionalfieldcorev1.SecretKeySelectorexposes anOptional *boolfield that no controller respects — thesecrets.GetValue()function always fails if the secret is missing regardless of that flagCustom SecretKeyRef definition
Already defined in
cmd/thv-operator/api/v1alpha1/mcpexternalauthconfig_types.go(lines 589-598):What to change
1. CRD type definitions (
cmd/thv-operator/api/v1alpha1/mcpregistry_types.go)Convert these 5 fields from
corev1.SecretKeySelectorto the customSecretKeyReftype (frommcpexternalauthconfig_types.go). Since all types are in the same package, no import is needed.GitAuthConfigPasswordSecretRefcorev1.SecretKeySelectorSecretKeyRefMCPRegistryDatabaseConfigDBAppUserPasswordSecretRefcorev1.SecretKeySelectorSecretKeyRefMCPRegistryDatabaseConfigDBMigrationUserPasswordSecretRefcorev1.SecretKeySelectorSecretKeyRefMCPRegistryOAuthProviderConfigClientSecretRef*corev1.SecretKeySelector*SecretKeyRefMCPRegistryOAuthProviderConfigAuthTokenRef*corev1.SecretKeySelector*SecretKeyRefNote: preserve pointer vs value semantics —
ClientSecretRefandAuthTokenRefare pointers (optional), the others are values (required).Important: Do NOT remove the
corev1import from this file — it's still used forcorev1.ConfigMapKeySelector(lines ~86, ~450) andcorev1.LocalObjectReference(line ~625).2. Secrets client (
cmd/thv-operator/pkg/kubernetes/secrets/secrets.go)Change the
GetValuemethod signature at line 50 from:to accept
nameandkeystrings directly:Update the body (lines 51, 56, 58) to use
nameandkeyinstead ofsecretRef.NameandsecretRef.Key. This decouples the shared utility from any specific ref type.3. Callers of
GetValue(cmd/thv-operator/pkg/registryapi/pgpass.go)Only 2 call sites (lines ~36 and ~43). Update from:
to:
Same for
DBMigrationUserPasswordSecretRef.4. Config builder (
cmd/thv-operator/pkg/registryapi/config/config.go)Two helper functions accept
*corev1.SecretKeySelector— change them to*SecretKeyRef:buildSecretFilePath(line ~764): change parameter from*corev1.SecretKeySelectorto*mcpv1alpha1.SecretKeyRef(or just*SecretKeyRefif in same package). The body accesses.Nameand.Keywhich exist on both types — no logic changes needed.buildGitPasswordFilePath(line ~549): same change.Also update field access at lines ~533, ~537 in
buildGitAuthConfig— these accessauth.PasswordSecretRef.Nameand.Keywhich work identically on the custom type.5. PodTemplateSpec builder (
cmd/thv-operator/pkg/registryapi/podtemplatespec.go)WithGitAuthMountfunction (line ~350): change parameter fromcorev1.SecretKeySelectorto the custom type. The body accesses.Nameand.Key— no logic changes needed.6. Test files
Update all test fixtures that construct
corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "..."}, Key: "..."}to the simplerSecretKeyRef{Name: "...", Key: "..."}:cmd/thv-operator/pkg/registryapi/pgpass_test.go(~lines 259-265)cmd/thv-operator/pkg/registryapi/config/config_test.go(~lines 498, 546, 584, 622, 1676, 2179, 2231)cmd/thv-operator/pkg/kubernetes/secrets/secrets_test.go(~lines 143, 166, 201, 246, 280) — update to passname, keystrings instead ofSecretKeySelector7. Regenerate
After all changes:
The deepcopy code in
zz_generated.deepcopy.gowill regenerate automatically — do not edit it manually.Notes
{"name": "...", "key": "..."}) so existing manifests continue to work without changes.Optionalfield fromcorev1.SecretKeySelectorwas never checked by any controller — removing it has no behavioral impact.SecretKeyReftype is defined inmcpexternalauthconfig_types.gobut is used across multiple CRDs. Consider whether it should be moved to a shared types file in the future, but that's out of scope for this issue.Generated with Claude Code