Skip to content

Standardize secret references to use custom SecretKeyRef across all CRDs #4540

@ChrisJBurns

Description

@ChrisJBurns

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    apiItems related to the APIgoPull requests that update go codegood first issueGood for newcomerskubernetesItems related to Kubernetesoperatortech-debt

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions