diff --git a/internal/audit/crd_config_refs.go b/internal/audit/crd_config_refs.go index 04de472d25..4a16cdd391 100644 --- a/internal/audit/crd_config_refs.go +++ b/internal/audit/crd_config_refs.go @@ -387,7 +387,7 @@ func kedaTriggerAuthConfigRefs(u *unstructured.Unstructured) []bp.ConfigObjectRe for _, ref := range mapsAt(u.Object, "spec", "configMapTargetRef") { addConfigMap(&refs, ns, stringValue(ref["name"])) } - addSecret(&refs, ns, stringAt(u.Object, "spec", "gcpSecretManager", "credentials", "clientSecret", "name")) + addSecret(&refs, ns, stringAt(u.Object, "spec", "gcpSecretManager", "credentials", "clientSecret", "valueFrom", "secretKeyRef", "name")) return refs } diff --git a/internal/audit/crd_config_refs_test.go b/internal/audit/crd_config_refs_test.go index c98aa4a22a..8a8c1acf55 100644 --- a/internal/audit/crd_config_refs_test.go +++ b/internal/audit/crd_config_refs_test.go @@ -186,10 +186,17 @@ func TestDynamicConfigObjectRefs(t *testing.T) { obj: map[string]any{"spec": map[string]any{ "secretTargetRef": []any{map[string]any{"name": "queue-secret"}}, "configMapTargetRef": []any{map[string]any{"name": "queue-config"}}, - "gcpSecretManager": map[string]any{"credentials": map[string]any{"clientSecret": map[string]any{"name": "gcp-secret"}}}, + "gcpSecretManager": map[string]any{"credentials": map[string]any{"clientSecret": map[string]any{"valueFrom": map[string]any{"secretKeyRef": map[string]any{"name": "gcp-secret", "key": "credentials.json"}}}}}, }}, want: refs(secret("app", "queue-secret"), configMap("app", "queue-config"), secret("app", "gcp-secret")), }, + { + name: "keda shallow GCP credentials are not a reference", + gvr: gvr("keda.sh", "v1alpha1", "triggerauthentications"), + ns: "app", + obj: map[string]any{"spec": map[string]any{"gcpSecretManager": map[string]any{"credentials": map[string]any{"clientSecret": map[string]any{"name": "fictional"}}}}}, + want: refs(), + }, { name: "prometheus servicemonitor refs", gvr: gvr("monitoring.coreos.com", "v1", "servicemonitors"), diff --git a/packages/k8s-ui/src/components/resources/renderers/KedaCredentials.test.tsx b/packages/k8s-ui/src/components/resources/renderers/KedaCredentials.test.tsx new file mode 100644 index 0000000000..4bff8b9e81 --- /dev/null +++ b/packages/k8s-ui/src/components/resources/renderers/KedaCredentials.test.tsx @@ -0,0 +1,85 @@ +// @vitest-environment jsdom +import { act } from 'react' +import { createRoot } from 'react-dom/client' +import { renderToStaticMarkup } from 'react-dom/server' +import { expect, it, vi } from 'vitest' +import { KedaTriggerAuthRenderer } from './KedaTriggerAuthRenderer' +Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true }) +const spec = { + secretTargetRef: [{ parameter: 'token', name: 'direct', key: 'token' }], + gcpSecretManager: { + credentials: { + clientSecret: { + valueFrom: { secretKeyRef: { name: 'iam', key: 'credentials.json' } }, + }, + }, + }, +} + +it('links the actual nested GCP credential Secret in the authentication namespace', async () => { + const element = document.createElement('div') + const root = createRoot(element) + const onNavigate = vi.fn() + try { + await act(async () => + root.render( + , + ), + ) + const button = [...element.querySelectorAll('button')].find(b => b.textContent === 'iam') + expect(button).toBeDefined() + await act(async () => button!.click()) + expect(onNavigate).toHaveBeenCalledWith({ + kind: 'secrets', + group: '', + namespace: 'app', + name: 'iam', + }) + expect(element.textContent).toContain('credentials.json') + } finally { + await act(async () => root.unmount()) + } +}) + +it('retains cluster-auth names without inventing an operator credential namespace', () => { + const html = renderToStaticMarkup( + {}} + />, + ) + const doc = new DOMParser().parseFromString(html, 'text/html') + expect(doc.body.textContent).toContain('iam') + expect(doc.body.textContent).toContain('direct') + expect([...doc.querySelectorAll('button')].some(b => b.textContent === 'iam' || b.textContent === 'direct')).toBe( + false, + ) +}) + +it('does not accept the previously invented shallow GCP reference shape', () => { + const html = renderToStaticMarkup( + , + ) + expect(html).not.toContain('fictional') +}) diff --git a/packages/k8s-ui/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx b/packages/k8s-ui/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx index 881ee51234..002144304d 100644 --- a/packages/k8s-ui/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +++ b/packages/k8s-ui/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx @@ -21,6 +21,8 @@ export function KedaTriggerAuthRenderer({ data, onNavigate }: KedaTriggerAuthRen const awsSecretManager = spec.awsSecretManager const gcpSecretManager = spec.gcpSecretManager const podIdentity = spec.podIdentity + const credentialRef = gcpSecretManager?.credentials?.clientSecret?.valueFrom?.secretKeyRef + const namespace = data.metadata?.namespace return ( <> @@ -48,7 +50,7 @@ export function KedaTriggerAuthRenderer({ data, onNavigate }: KedaTriggerAuthRen
{ref.parameter} - Secret: / {ref.key} + Secret: {namespace ? : ref.name} / {ref.key}
@@ -150,9 +152,10 @@ export function KedaTriggerAuthRenderer({ data, onNavigate }: KedaTriggerAuthRen {gcpSecretManager && (
- {gcpSecretManager.credentials?.clientSecret?.name && ( - + {credentialRef?.name && ( + : credentialRef.name} /> )} + {credentialRef?.key && } {gcpSecretManager.secrets && gcpSecretManager.secrets.length > 0 && (