Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cmd/thv-operator/api/v1alpha1/mcpregistry_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ type GitAuthConfig struct {
// key: token
//
// +kubebuilder:validation:Required
PasswordSecretRef corev1.SecretKeySelector `json:"passwordSecretRef"`
PasswordSecretRef SecretKeyRef `json:"passwordSecretRef"`
}

// APISource defines API source configuration for ToolHive Registry APIs
Expand Down Expand Up @@ -344,14 +344,14 @@ type MCPRegistryDatabaseConfig struct {
// that is mounted to the registry API container.
//
// +kubebuilder:validation:Required
DBAppUserPasswordSecretRef corev1.SecretKeySelector `json:"dbAppUserPasswordSecretRef"`
DBAppUserPasswordSecretRef SecretKeyRef `json:"dbAppUserPasswordSecretRef"`

// DBMigrationUserPasswordSecretRef references a Kubernetes Secret containing the password for the migration database user.
// The operator will use this password along with DBAppUserPasswordSecretRef to generate a pgpass file
// that is mounted to the registry API container.
//
// +kubebuilder:validation:Required
DBMigrationUserPasswordSecretRef corev1.SecretKeySelector `json:"dbMigrationUserPasswordSecretRef"`
DBMigrationUserPasswordSecretRef SecretKeyRef `json:"dbMigrationUserPasswordSecretRef"`
}

// MCPRegistryAuthMode represents the authentication mode for the registry API server
Expand Down Expand Up @@ -441,7 +441,7 @@ type MCPRegistryOAuthProviderConfig struct {
// ClientSecretRef is a reference to a Secret containing the client secret
// The secret should have a key "clientSecret" containing the secret value
// +optional
ClientSecretRef *corev1.SecretKeySelector `json:"clientSecretRef,omitempty"`
ClientSecretRef *SecretKeyRef `json:"clientSecretRef,omitempty"`

// CACertRef is a reference to a ConfigMap containing the CA certificate bundle
// for verifying the provider's TLS certificate.
Expand All @@ -458,7 +458,7 @@ type MCPRegistryOAuthProviderConfig struct {
// to OIDC/JWKS endpoints. Useful when the OIDC discovery or JWKS endpoint requires authentication.
// Example: ServiceAccount token for Kubernetes API server
// +optional
AuthTokenRef *corev1.SecretKeySelector `json:"authTokenRef,omitempty"`
AuthTokenRef *SecretKeyRef `json:"authTokenRef,omitempty"`

// AuthTokenFile is the path to a file containing a bearer token for authenticating to OIDC/JWKS endpoints.
// Useful when the OIDC discovery or JWKS endpoint requires authentication.
Expand Down
8 changes: 4 additions & 4 deletions cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/thv-operator/pkg/kubernetes/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// kubeClient := kubernetes.NewClient(ctrlClient, scheme)
//
// // Access secrets operations via the Secrets field
// value, err := kubeClient.Secrets.GetValue(ctx, "default", secretKeySelector)
// value, err := kubeClient.Secrets.GetValue(ctx, "default", "secret-name", "secret-key")
//
// // Upsert a secret with owner reference
// result, err := kubeClient.Secrets.UpsertWithOwnerReference(ctx, secret, ownerObject)
Expand Down
9 changes: 4 additions & 5 deletions cmd/thv-operator/pkg/kubernetes/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,16 @@ func (c *Client) Get(ctx context.Context, name, namespace string) (*corev1.Secre
}

// GetValue retrieves a specific key's value from a Kubernetes Secret.
// Uses a SecretKeySelector to identify the secret name and key.
// Returns the value as a string, or an error if the secret or key is not found.
func (c *Client) GetValue(ctx context.Context, namespace string, secretRef corev1.SecretKeySelector) (string, error) {
secret, err := c.Get(ctx, secretRef.Name, namespace)
func (c *Client) GetValue(ctx context.Context, namespace, name, key string) (string, error) {
secret, err := c.Get(ctx, name, namespace)
if err != nil {
return "", err
}

value, exists := secret.Data[secretRef.Key]
value, exists := secret.Data[key]
if !exists {
return "", fmt.Errorf("key %s not found in secret %s", secretRef.Key, secretRef.Name)
return "", fmt.Errorf("key %s not found in secret %s", key, name)
}

return string(value), nil
Expand Down
45 changes: 5 additions & 40 deletions cmd/thv-operator/pkg/kubernetes/secrets/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,7 @@ func TestGetValue(t *testing.T) {
Build()

client := NewClient(fakeClient, scheme)
secretRef := corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "test-secret",
},
Key: "password",
}

value, err := client.GetValue(ctx, "default", secretRef)
value, err := client.GetValue(ctx, "default", "test-secret", "password")

require.NoError(t, err)
assert.Equal(t, "super-secret-password", value)
Expand All @@ -163,14 +156,7 @@ func TestGetValue(t *testing.T) {
Build()

client := NewClient(fakeClient, scheme)
secretRef := corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "non-existent-secret",
},
Key: "password",
}

value, err := client.GetValue(ctx, "default", secretRef)
value, err := client.GetValue(ctx, "default", "non-existent-secret", "password")

require.Error(t, err)
assert.Empty(t, value)
Expand Down Expand Up @@ -198,14 +184,7 @@ func TestGetValue(t *testing.T) {
Build()

client := NewClient(fakeClient, scheme)
secretRef := corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "test-secret",
},
Key: "non-existent-key",
}

value, err := client.GetValue(ctx, "default", secretRef)
value, err := client.GetValue(ctx, "default", "test-secret", "non-existent-key")

require.Error(t, err)
assert.Empty(t, value)
Expand Down Expand Up @@ -243,14 +222,7 @@ func TestGetValue(t *testing.T) {
Build()

client := NewClient(fakeClient, scheme)
secretRef := corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "test-secret",
},
Key: "password",
}

value, err := client.GetValue(ctx, "namespace2", secretRef)
value, err := client.GetValue(ctx, "namespace2", "test-secret", "password")

require.NoError(t, err)
assert.Equal(t, "password2", value)
Expand All @@ -277,14 +249,7 @@ func TestGetValue(t *testing.T) {
Build()

client := NewClient(fakeClient, scheme)
secretRef := corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "test-secret",
},
Key: "empty-key",
}

value, err := client.GetValue(ctx, "default", secretRef)
value, err := client.GetValue(ctx, "default", "test-secret", "empty-key")

require.NoError(t, err)
assert.Empty(t, value)
Expand Down
4 changes: 2 additions & 2 deletions cmd/thv-operator/pkg/registryapi/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ func buildGitAuthConfig(auth *mcpv1alpha1.GitAuthConfig) (*GitAuthConfig, error)

// buildGitPasswordFilePath constructs the file path where a git password secret will be mounted.
// The secretRef must have both Name and Key set (validated by buildGitAuthConfig).
func buildGitPasswordFilePath(secretRef *corev1.SecretKeySelector) string {
func buildGitPasswordFilePath(secretRef *mcpv1alpha1.SecretKeyRef) string {
if secretRef == nil {
return ""
}
Expand Down Expand Up @@ -761,7 +761,7 @@ func buildOAuthProviderConfig(
}

// buildSecretFilePath constructs the file path where a secret will be mounted
func buildSecretFilePath(secretRef *corev1.SecretKeySelector) string {
func buildSecretFilePath(secretRef *mcpv1alpha1.SecretKeyRef) string {
if secretRef == nil {
return ""
}
Expand Down
70 changes: 26 additions & 44 deletions cmd/thv-operator/pkg/registryapi/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,11 +495,9 @@ func TestBuildConfig_GitAuth(t *testing.T) {
Path: "registry.json",
Auth: &mcpv1alpha1.GitAuthConfig{
Username: "git",
PasswordSecretRef: corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "git-credentials",
},
Key: "token",
PasswordSecretRef: mcpv1alpha1.SecretKeyRef{
Name: "git-credentials",
Key: "token",
},
},
},
Expand Down Expand Up @@ -543,10 +541,8 @@ func TestBuildConfig_GitAuth(t *testing.T) {
Path: "registry.json",
Auth: &mcpv1alpha1.GitAuthConfig{
Username: "git",
PasswordSecretRef: corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "git-credentials",
},
PasswordSecretRef: mcpv1alpha1.SecretKeyRef{
Name: "git-credentials",
// Key is empty - should cause an error
},
},
Expand Down Expand Up @@ -581,11 +577,9 @@ func TestBuildConfig_GitAuth(t *testing.T) {
Path: "registry.json",
Auth: &mcpv1alpha1.GitAuthConfig{
// Username is empty - should cause an error
PasswordSecretRef: corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "git-credentials",
},
Key: "token",
PasswordSecretRef: mcpv1alpha1.SecretKeyRef{
Name: "git-credentials",
Key: "token",
},
},
},
Expand Down Expand Up @@ -619,11 +613,9 @@ func TestBuildConfig_GitAuth(t *testing.T) {
Path: "registry.json",
Auth: &mcpv1alpha1.GitAuthConfig{
Username: "git",
PasswordSecretRef: corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "", // Empty name should cause an error
},
Key: "token",
PasswordSecretRef: mcpv1alpha1.SecretKeyRef{
Name: "", // Empty name should cause an error
Key: "token",
},
},
},
Expand Down Expand Up @@ -1673,11 +1665,9 @@ func TestBuildConfig_AuthConfig(t *testing.T) {
IssuerURL: "https://keycloak.example.com/realms/myrealm",
Audience: "registry-api",
ClientID: "registry-client",
ClientSecretRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "keycloak-secret",
},
Key: "client-secret",
ClientSecretRef: &mcpv1alpha1.SecretKeyRef{
Name: "keycloak-secret",
Key: "client-secret",
},
CACertRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Expand Down Expand Up @@ -1984,23 +1974,19 @@ func TestBuildSecretFilePath(t *testing.T) {

t.Run("secret ref with key", func(t *testing.T) {
t.Parallel()
secretRef := &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "my-secret",
},
Key: "my-key",
secretRef := &mcpv1alpha1.SecretKeyRef{
Name: "my-secret",
Key: "my-key",
}
result := buildSecretFilePath(secretRef)
assert.Equal(t, "/secrets/my-secret/my-key", result)
})

t.Run("secret ref without key uses default", func(t *testing.T) {
t.Parallel()
secretRef := &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "my-secret",
},
Key: "",
secretRef := &mcpv1alpha1.SecretKeyRef{
Name: "my-secret",
Key: "",
}
result := buildSecretFilePath(secretRef)
assert.Equal(t, "/secrets/my-secret/clientSecret", result)
Expand Down Expand Up @@ -2176,11 +2162,9 @@ func TestBuildOAuthProviderConfig_DirectPaths(t *testing.T) {
IssuerURL: "https://issuer.example.com",
Audience: "my-app",
AuthTokenFile: "/var/run/secrets/kubernetes.io/serviceaccount/token", // Direct path
AuthTokenRef: &corev1.SecretKeySelector{ // Should be ignored
LocalObjectReference: corev1.LocalObjectReference{
Name: "token-secret",
},
Key: "token",
AuthTokenRef: &mcpv1alpha1.SecretKeyRef{ // Should be ignored
Name: "token-secret",
Key: "token",
},
},
},
Expand Down Expand Up @@ -2228,11 +2212,9 @@ func TestBuildOAuthProviderConfig_DirectPaths(t *testing.T) {
IssuerURL: "https://issuer.example.com",
Audience: "my-app",
// AuthTokenFile not set
AuthTokenRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "token-secret",
},
Key: "my-token",
AuthTokenRef: &mcpv1alpha1.SecretKeyRef{
Name: "token-secret",
Key: "my-token",
},
},
},
Expand Down
24 changes: 9 additions & 15 deletions cmd/thv-operator/pkg/registryapi/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,9 @@ func TestManagerBuildRegistryAPIDeployment(t *testing.T) {
Path: "registry.json",
Auth: &mcpv1alpha1.GitAuthConfig{
Username: "git",
PasswordSecretRef: corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "git-credentials",
},
Key: "token",
PasswordSecretRef: mcpv1alpha1.SecretKeyRef{
Name: "git-credentials",
Key: "token",
},
},
},
Expand Down Expand Up @@ -260,11 +258,9 @@ func TestManagerBuildRegistryAPIDeployment(t *testing.T) {
Path: "registry.json",
Auth: &mcpv1alpha1.GitAuthConfig{
Username: "git",
PasswordSecretRef: corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "git-credentials-1",
},
Key: "token",
PasswordSecretRef: mcpv1alpha1.SecretKeyRef{
Name: "git-credentials-1",
Key: "token",
},
},
},
Expand All @@ -278,11 +274,9 @@ func TestManagerBuildRegistryAPIDeployment(t *testing.T) {
Path: "registry.json",
Auth: &mcpv1alpha1.GitAuthConfig{
Username: "git",
PasswordSecretRef: corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "git-credentials-2",
},
Key: "password",
PasswordSecretRef: mcpv1alpha1.SecretKeyRef{
Name: "git-credentials-2",
Key: "password",
},
},
},
Expand Down
13 changes: 11 additions & 2 deletions cmd/thv-operator/pkg/registryapi/pgpass.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,24 @@ func (m *manager) ensurePGPassSecret(
dbConfig := mcpRegistry.GetDatabaseConfig()

// Read app user password from secret
appUserPassword, err := m.kubeHelper.Secrets.GetValue(ctx, mcpRegistry.Namespace, dbConfig.DBAppUserPasswordSecretRef)
appUserPassword, err := m.kubeHelper.Secrets.GetValue(
ctx,
mcpRegistry.Namespace,
dbConfig.DBAppUserPasswordSecretRef.Name,
dbConfig.DBAppUserPasswordSecretRef.Key,
)
if err != nil {
return fmt.Errorf("failed to read app user password from secret %s: %w",
dbConfig.DBAppUserPasswordSecretRef.Name, err)
}

// Read migration user password from secret
migrationUserPassword, err := m.kubeHelper.Secrets.GetValue(
ctx, mcpRegistry.Namespace, dbConfig.DBMigrationUserPasswordSecretRef)
ctx,
mcpRegistry.Namespace,
dbConfig.DBMigrationUserPasswordSecretRef.Name,
dbConfig.DBMigrationUserPasswordSecretRef.Key,
)
if err != nil {
return fmt.Errorf("failed to read migration user password from secret %s: %w",
dbConfig.DBMigrationUserPasswordSecretRef.Name, err)
Expand Down
Loading
Loading