-
Notifications
You must be signed in to change notification settings - Fork 606
frontend: Container env vars: show secret value for secretKeyRef #4706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -27,6 +27,7 @@ import Paper from '@mui/material/Paper'; | |||||
| import { BaseTextFieldProps } from '@mui/material/TextField'; | ||||||
| import Typography from '@mui/material/Typography'; | ||||||
| import { useTheme } from '@mui/system'; | ||||||
| import { useQueries } from '@tanstack/react-query'; | ||||||
| import { Location } from 'history'; | ||||||
| import { Base64 } from 'js-base64'; | ||||||
| import { JSONPath } from 'jsonpath-plus'; | ||||||
|
|
@@ -38,6 +39,10 @@ import { generatePath, NavLinkProps, useLocation } from 'react-router-dom'; | |||||
| import YAML from 'yaml'; | ||||||
| import { labelSelectorToQuery, ResourceClasses, useCluster } from '../../../lib/k8s'; | ||||||
| import { ApiError } from '../../../lib/k8s/api/v2/ApiError'; | ||||||
| import { clusterFetch } from '../../../lib/k8s/api/v2/fetch'; | ||||||
| import { useEndpoints } from '../../../lib/k8s/api/v2/hooks'; | ||||||
| import { KubeObjectEndpoint } from '../../../lib/k8s/api/v2/KubeObjectEndpoint'; | ||||||
| import { makeUrl } from '../../../lib/k8s/api/v2/makeUrl'; | ||||||
| import { KubeCondition, KubeContainer, KubeContainerStatus } from '../../../lib/k8s/cluster'; | ||||||
| import ConfigMap from '../../../lib/k8s/configMap'; | ||||||
| import { KubeEvent } from '../../../lib/k8s/event'; | ||||||
|
|
@@ -751,46 +756,45 @@ function extractEnvVarReferences(container: KubeContainer): EnvVarReference[] { | |||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Component that fetches a Secret and reports the result. | ||||||
| * This properly calls hooks at the top level. | ||||||
| * Fetches a list of KubeObjects by name using useQueries (fetch-only, no WebSocket watches). | ||||||
| * Returns a Map<name, FetchedResource> compatible with buildEnvironmentVariables. | ||||||
| */ | ||||||
| function SecretFetcher(props: { | ||||||
| name: string; | ||||||
| namespace: string; | ||||||
| onResult: (name: string, resource: KubeObject | null, error: ApiError | null) => void; | ||||||
| }) { | ||||||
| const { name, namespace, onResult } = props; | ||||||
| const [secret, error] = Secret.useGet(name, namespace); | ||||||
|
|
||||||
| React.useEffect(() => { | ||||||
| // Only call onResult when we have a definitive result (either data or error) | ||||||
| if (secret || error) { | ||||||
| onResult(name, secret, error); | ||||||
| } | ||||||
| }, [secret, error, name, onResult]); | ||||||
|
|
||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Component that fetches a ConfigMap and reports the result. | ||||||
| * This properly calls hooks at the top level. | ||||||
| */ | ||||||
| function ConfigMapFetcher(props: { | ||||||
| name: string; | ||||||
| namespace: string; | ||||||
| onResult: (name: string, resource: KubeObject | null, error: ApiError | null) => void; | ||||||
| }) { | ||||||
| const { name, namespace, onResult } = props; | ||||||
| const [configMap, error] = ConfigMap.useGet(name, namespace); | ||||||
|
|
||||||
| React.useEffect(() => { | ||||||
| if (configMap || error) { | ||||||
| onResult(name, configMap, error); | ||||||
| } | ||||||
| }, [configMap, error, name, onResult]); | ||||||
| function useResourcesFetchOnly( | ||||||
| kubeObjectClass: typeof Secret | typeof ConfigMap, | ||||||
| names: string[], | ||||||
| namespace: string, | ||||||
| cluster: string | ||||||
| ): Map<string, FetchedResource> { | ||||||
| const { endpoint } = useEndpoints(kubeObjectClass.apiEndpoint.apiInfo, cluster); | ||||||
|
|
||||||
| const results = useQueries({ | ||||||
| queries: names.map(name => ({ | ||||||
| queryKey: ['env-resource', cluster, namespace, kubeObjectClass.apiName, name], | ||||||
| enabled: !!endpoint, | ||||||
| staleTime: 30_000, | ||||||
| queryFn: async (): Promise<FetchedResource> => { | ||||||
| try { | ||||||
| const url = makeUrl([KubeObjectEndpoint.toUrl(endpoint!, namespace), name]); | ||||||
| const obj = await clusterFetch(url, { cluster }).then(r => r.json()); | ||||||
| return { resource: new kubeObjectClass(obj) as KubeObject, error: null }; | ||||||
| } catch (err) { | ||||||
| return { resource: null, error: err as ApiError }; | ||||||
| } | ||||||
| }, | ||||||
| })), | ||||||
| }); | ||||||
|
|
||||||
| return null; | ||||||
| return React.useMemo(() => { | ||||||
| const map = new Map<string, FetchedResource>(); | ||||||
| names.forEach((name, idx) => { | ||||||
| const result = results[idx]; | ||||||
| if (result?.data) { | ||||||
| map.set(name, result.data); | ||||||
| } | ||||||
| }); | ||||||
| return map; | ||||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||||||
| }, [names, ...results.map(r => r.data)]); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
|
|
@@ -1027,36 +1031,10 @@ export function ContainerEnvironmentVariables(props: EnvironmentVariablesProps) | |||||
| const { pod, container } = props; | ||||||
| const { t } = useTranslation(); | ||||||
| const { enqueueSnackbar } = useSnackbar(); | ||||||
|
|
||||||
| // State to store fetched resources | ||||||
| const [fetchedSecrets, setFetchedSecrets] = React.useState<Map<string, FetchedResource>>( | ||||||
| new Map() | ||||||
| ); | ||||||
| const [fetchedConfigMaps, setFetchedConfigMaps] = React.useState<Map<string, FetchedResource>>( | ||||||
| new Map() | ||||||
| ); | ||||||
|
|
||||||
| // Early return if no env vars | ||||||
| if ( | ||||||
| (!container?.env && !container?.envFrom) || | ||||||
| !pod?.status?.containerStatuses || | ||||||
| !pod?.metadata?.namespace | ||||||
| ) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| const namespace = pod.metadata.namespace; | ||||||
| const containerStartTimestamp = (() => { | ||||||
| let timestamp = pod.metadata?.creationTimestamp; | ||||||
| const containerStatus = pod.status?.containerStatuses?.find(c => c.name === container?.name); | ||||||
| if (containerStatus?.started && containerStatus.state?.running?.startedAt) { | ||||||
| timestamp = containerStatus.state.running.startedAt; | ||||||
| } | ||||||
| return timestamp; | ||||||
| })(); | ||||||
| const cluster = useCluster() ?? ''; | ||||||
|
|
||||||
| // Extract all references upfront (pure function, no hooks) | ||||||
| const references = extractEnvVarReferences(container); | ||||||
| const references = extractEnvVarReferences(container as KubeContainer); | ||||||
|
||||||
| const references = extractEnvVarReferences(container as KubeContainer); | |
| const references = extractEnvVarReferences(container); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth a look. Usually it is a good idea to avoid using as because generally it hides bugs. Sometimes you need to handle the cases it brings up.
In rare cases it can be there’s a reason the types don’t work or the types are wrong and as can work around that. If this is one of those cases please add a comment there explaining the issue. Then someone later can get to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency array contains a spread of
results.map(r => r.data), which creates a new array on every render. This causes the useMemo to recompute on every render, defeating its purpose. Consider using a different approach such as:combineoption inuseQueriesto transform the results directly (similar to the pattern inuseKubeObjectList.ts)result.datato the dependency array without spreading, though this is verboseThe current implementation with the eslint-disable comment suggests this was an intentional workaround, but it's worth reconsidering for performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems valid.