bug: Dynamic filter rules on PodConfig cause flickering or incorrectly reveal hidden options
- Dominant language
- No language data
- Stars
- 84
- Forks
- 149
- Avg merge
- 5d 15h
- Merged PRs (30d)
- 29
Description
### Checks
- [x] I have searched the [existing issues](https://github.com/kubeflow/notebooks/issues).
- [x] My issue is related to one of the components in the [`kubeflow/notebooks`](https://github.com/kubeflow/notebooks) repository.
### Kubeflow Notebooks Version
v2.0.0-beta.0
### Kubeflow Platform
Kubeflow Distributions
### Kubernetes Distribution
GKE
### Kubernetes Version
```shell
Client Version: v1.36.1
Kustomize Version: v5.8.1
Server Version: v1.35.6-gke.1258000
```
### Description
### **What happened?**
When defining a `filterRule` on `WorkspaceKind` to hide non-GPU `podConfigs` when a CUDA image is selected:
1. **Infinite Re-render Loop / UI Flickering with `api.hide: true`:**
When using both `ui.hide: true` and `api.hide: true` in the rule, selecting a CUDA image in the Spawner UI causes the form to flicker rapidly and get stuck in an infinite state update loop. The user is unable to select the remaining GPU pod config or proceed to create a workspace.
2. **Hidden PodConfigs Automatically Revealed with only `ui.hide: true`:**
When removing `api.hide: true` (relying only on `ui.hide: true`), user can proceed to create the GPU workspace, but the default CPU pod configs are not hidden in the UI even though the user has not selected "Show hidden". The "Show hidden" filter is automatically enabled.
---
### **What did you expect to happen?**
1. Selecting a CUDA image should cleanly filter the pod configs so that only compatible GPU pod configs are shown (and selectable) without causing UI flickering or infinite re-renders.
2. If `ui.hide: true` is set on CPU configs for a CUDA image, those CPU configs should remain hidden by default unless the user explicitly checks "Show hidden".
---
### **How to reproduce it?**
1. add the following `filterRules` in `kubeflow-notebooks/workspaces/controller/manifests/kustomize/samples/jupyterlab_v1beta1_workspacekind.yaml`:
```yaml
- scope: POD_CONFIG
effect:
ui:
hide: true
api:
hide: true
match:
- matchImageConfig:
selector:
matchExpressions:
- key: "cuda_version"
operator: Exists
- matchPodConfig:
selector:
matchExpressions:
- key: "gpu"
operator: DoesNotExist
```
2. Open the Workspace Spawner UI (`/workspaces/form/create`).
3. In Step 1, select the `jupyterlab` WorkspaceKind.
4. In Step 2, select the CUDA image (`jupyter-pytorch-cuda-full:v1.10.0`) and click **Next**.
5. **Observe Behavior 1**: The UI starts flickering indefinitely and pod config cards cannot be selected.
6. Remove `api.hide: true` from the `filterRules` and reload the form.
7. Select the CUDA image and proceed to Step 3.
8. **Observe Behavior 2**: "Tiny CPU" is shown in the list by default even though the rule specifies `ui.hide: true` and the user did not check "Show hidden".
---
### **Root Cause Analysis (Generated by Gemini)**
#### 1. Infinite Loop in `WorkspaceForm.tsx` (Triggered when `api.hide: true`)
- In `WorkspaceForm.tsx`:
- **Effect 1 (Defaulting)** sets `podConfig` to `allValuesData.podConfig.default` (`tiny_cpu`) whenever `!data.podConfig`:
```tsx
useEffect(() => {
if (!allValuesData || !allValuesLoaded || !data.kind) return;
if (!data.podConfig && allValuesData.podConfig.default) {
setData('podConfig', allValuesData.podConfig.default);
}
}, [allValuesData, allValuesLoaded, data.kind, data.imageConfig, data.podConfig, setData]);
```
- **Effect 2 (Validation)** clears `data.podConfig` when `filteredValuesData` arrives and the selected pod config is no longer valid:
```tsx
useEffect(() => {
if (!filteredValuesLoaded || !filteredValuesData || !data.podConfig) return;
const podConfigOptions = filteredValuesData.podConfig.values ?? [];
const isStillValid = podConfigOptions.some((pc) => pc.id === data.podConfig);
if (!isStillValid) {
setData('podConfig', undefined);
}
}, [filteredValuesData, filteredValuesLoaded, data.podConfig, setData]);
```
- When `api.hide: true` removes `tiny_cpu` from `filteredValuesData`, Effect 2 sets `data.podConfig = undefined`. Because `data.podConfig` is in Effect 1's dependencies, Effect 1 immediately sets it back to `"tiny_cpu"`, which re-triggers Effect 2, creating an endless ping-pong re-render loop (`undefined` $\leftrightarrow$ `"tiny_cpu"`).
#### 2. Auto-enabling "Show hidden" in `filterDefaults.ts` (Triggered when only `ui.hide: true`)
- In `WorkspaceFormPodConfigSelection.tsx` and `filterDefaults.ts`:
```ts
export const computeDefaultFilterValues = (options, defaultId) => {
const defaultOption = options.find((opt) => opt.id === defaultId);
return {
showHidden: defaultOption?.hidden ?? false,
showRedirected: defaultOption?.redirect !== undefined,
};
};
```
- Because the backend returns the static default ID (`tiny_cpu`), and `tiny_cpu` has `hidden: true` due to `ui.hide`, `computeDefaultFilterValues` sets `showHidden: true` by default.
- Additionally, `selectedPodConfig` is initially set to `tiny_cpu` (which is `hidden: true`), forcing `showHidden = true`.
#### 3. Backend `/listvalues` returns static default
- In `workspaces/backend/internal/models/workspacekinds/podtemplate/options/funcs.go`, `/listvalues` statically returns `wsk.Spec.PodTemplate.Options.PodConfig.Spawner.Default` without validating if that default option was hidden or removed by filter rules in the requested context.
### Relevant Logs
```shell
```
Contributor guide
Assessment
This issue has not been assessed yet.