Skip to content

feat: Apply filterrule for /workspacekinds API - #1357

Open
gangli113 wants to merge 2 commits into
kubeflow:notebooks-v2from
gangli113:feat-wsk-filterrules-workspacekinds
Open

feat: Apply filterrule for /workspacekinds API#1357
gangli113 wants to merge 2 commits into
kubeflow:notebooks-v2from
gangli113:feat-wsk-filterrules-workspacekinds

Conversation

@gangli113

Copy link
Copy Markdown

Closes #847

Summary

Implements compatibility selectors for the /workspacekinds API (issue #847),
building on the shared filterrules evaluation engine introduced in #846.

When a namespaceFilter is supplied, each WorkspaceKind's spec.filterRules[] with
scope: WORKSPACE_KIND are evaluated (first-match-wins) against the namespace's labels
to compute the per-WorkspaceKind hidden flag and restrictions, and to omit
api.hide'd kinds from the response entirely.

Changes

  • filterrules engine
    • Add EvaluateWorkspaceFilterScopeRule(wsk, namespaceLabels) to evaluate
      WORKSPACE_KIND-scoped rules against namespace labels.
    • Add BuildEvalContextForImageAndPodCfg(...) wrapper for the /listvalues path
      (IMAGE_CONFIG + POD_CONFIG scopes) and make buildEvalContext unexported.
    • Add filterRulesByScope so each endpoint only compiles the scopes it evaluates.
  • Model builder (NewWorkspaceKindModelFromWorkspaceKind)
    • Evaluate rules first; return early (apiHide=true) when api.hide matches.
    • Merge admin-set hidden with the ui.hide effect (logical OR) and surface
      restrictions from api.deny.
  • Repository (GetWorkspaceKinds)
    • Resolve namespace labels from namespaceFilter and omit api.hide'd kinds.
  • Handler: pass the request namespace through to the repository.

Behavior

Scenario Result
No namespaceFilter (admin listing) matchNamespace rules do not fire; admin-set hidden preserved, no deny
ui.hide matches namespace hidden = adminHidden || true
api.hide matches namespace WorkspaceKind omitted from response
api.deny matches namespace restrictions populated with deny + denyMessage

Testing

Added unit tests at three layers:

  • EngineEvaluateWorkspaceFilterScopeRule and BuildEvalContextForImageAndPodCfg.
  • Modelui.hide / api.hide / api.deny via matchNamespace, plus admin-listing.
  • Repository — end-to-end with a fake client (ui.hide, api.hide omission, api.deny,
    missing namespace).

All packages pass:

  • internal/filterrules
  • internal/models/workspacekinds
  • internal/repositories/workspacekinds

Evaluate WORKSPACE_KIND-scoped filterRules against the request's
namespaceFilter to compute per-WorkspaceKind hidden/restrictions and to
omit api.hide'd kinds from the /workspacekinds response.

Adds EvaluateWorkspaceFilterScopeRule and a purpose-built
BuildEvalContextForImageAndPodCfg wrapper to the filterrules engine,
wires evaluation through the model builder and repository, and adds unit
tests at the engine, model, and repository layers.

Part of kubeflow#847

Signed-off-by: Gang Li <ganglica@google.com>
@github-project-automation github-project-automation Bot moved this to Needs Triage in Kubeflow Notebooks Aug 28, 2026
@google-oss-prow google-oss-prow Bot added the area/backend area - related to backend components label Aug 28, 2026
@google-oss-prow google-oss-prow Bot added area/v2 area - version - kubeflow notebooks v2 size/XL labels Aug 28, 2026
@gangli113

Copy link
Copy Markdown
Author

/cc @andyatmiami

@andyatmiami andyatmiami left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gangli113 - Another excellent/quality contribution...

Minor nits and clarifications on some aspects of code - but nothing truly concerning...

Another thing we need to pay attention to is the Slack thread I started in #kubeflow-notebooks as I realized the proposal/spec did not do a good job outlining expected behavior for namespaceFilter referencing a non-existent workspace (which then also probably could affect the namespace attribute in ListValuesRequest)

But feel free to address all other comments (and/or push back/discuss if you take issue with any comments I made).

I am comfortable proceeding to merge this PR if everything else gets resolved and the Slack thread doesn't have consensus - because we can always address that in a follow up PR.

Thanks!

Comment thread workspaces/backend/api/workspacekinds_handler_test.go
Comment thread workspaces/backend/api/workspacekinds_handler_test.go
Comment thread workspaces/backend/api/workspacekinds_handler_test.go Outdated
Comment thread workspaces/backend/api/workspacekinds_handler_test.go Outdated
Comment thread workspaces/backend/internal/filterrules/engine.go Outdated
Comment thread workspaces/backend/internal/filterrules/engine.go Outdated
Comment thread workspaces/backend/internal/filterrules/engine.go Outdated

@christian-heusel christian-heusel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/ok-to-test

- rename EvaluateWorkspaceFilterScopeRule to EvaluateWorkspaceKindFilterScopeRule
- extract read-only scope sets to package-level vars
- return 422 when namespaceFilter references a non-existent namespace
- assert apiHide return values and add filterRules-defined handler tests
- add EvaluateWorkspaceKindFilterScopeRule unit tests

Part of kubeflow#847

Signed-off-by: Gang Li <ganglica@google.com>
@google-oss-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please ask for approval from andyatmiami. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@gangli113
gangli113 requested a review from andyatmiami August 28, 2026 22:14
@gangli113

Copy link
Copy Markdown
Author

@andyatmiami thanks for the review comments. I have resolved all your comments. Also changed the bechavior of resolveNamespace() that both ListValues and workspacekinds API can return error 422 when namespace doesn't exist in the cluster.

@andyatmiami andyatmiami left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @gangli113 - thanks for the quick turnaround on the last round of comments...

getting real close to having this merge ready - just a few more rough edges to polish.

in addition to the PR comments below - I did also notice something else we missed on last PR (i think) - hoping we can simply sneak it in here to knock it out (see below)


Missing return after serverErrorResponse in PodTemplateOptionsListValuesHandler (workspacekind_podtemplate_options_handler.go:144) — falls through to
dataResponse, writing two HTTP responses.

if err := r.client.Get(ctx, client.ObjectKey{Name: namespaceName}, namespace); err != nil {
if apierrors.IsNotFound(err) {
return nil, nil
namespacePath := field.NewPath("namespace")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation error field path does not match either caller's user-facing parameter

resolveNamespaceLabels is shared by two callers with different user-facing parameter names:

  • GetWorkspaceKinds: the client sends query param namespaceFilter (handler validates format with field.NewPath(constants.NamespaceFilterQueryParam) at line 114)
  • ListPodTemplateOptionsValues: the client sends context.namespace.name in the request body

The hardcoded "namespace" matches neither. Clients receiving a 422 with field: "namespace" cannot identify which input was invalid.

Proposed Fix: Accept a *field.Path parameter so each caller passes the correct path:

func (r *WorkspaceKindRepository) resolveNamespaceLabels(ctx context.Context, namespaceName string, fieldPath *field.Path) (map[string]string, error) {
  • GetWorkspaceKinds passes field.NewPath(constants.NamespaceFilterQueryParam)
  • ListPodTemplateOptionsValues passes field.NewPath("context", "namespace", "name")

})
})

var _ = Describe("EvaluateWorkspaceFilterScopeRule", func() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var _ = Describe("EvaluateWorkspaceFilterScopeRule", func() {
var _ = Describe("EvaluateWorkspaceKindFilterScopeRule", func() {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/backend area - related to backend components area/v2 area - version - kubeflow notebooks v2 ok-to-test size/XL

Projects

Status: Needs Triage

Development

Successfully merging this pull request may close these issues.

3 participants