From 5bbb5f439a5e11442f9db0e49abd0a0d5541e4bc Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:48:25 -0600 Subject: [PATCH 1/2] support KubeSpawner profile_options --- .../stages/kubernetes_services/__init__.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/_nebari/stages/kubernetes_services/__init__.py b/src/_nebari/stages/kubernetes_services/__init__.py index fdc413bd40..8c6190eb1a 100644 --- a/src/_nebari/stages/kubernetes_services/__init__.py +++ b/src/_nebari/stages/kubernetes_services/__init__.py @@ -109,6 +109,34 @@ class KubeSpawner(schema.Base): model_config = ConfigDict(extra="allow") +class ProfileOptionUnlistedChoice(schema.Base): + enabled: bool = False + display_name: str + display_name_in_choices: Optional[str] = None + validation_regex: Optional[str] = None + validation_message: Optional[str] = None + kubespawner_override: Dict[str, Any] + + +class ProfileOptionChoice(schema.Base): + display_name: str + default: Optional[bool] = False + kubespawner_override: Dict[str, Any] + + +class ProfileOption(schema.Base): + display_name: str + unlisted_choice: Optional[ProfileOptionUnlistedChoice] = None + choices: Dict[str, ProfileOptionChoice] + + @field_validator("choices") + def validate_choices(cls, v): + defaults = [choice for choice in v.values() if choice.default] + if len(defaults) > 1: + raise ValueError("Only one choice can be marked as default") + return v + + class JupyterLabProfile(schema.Base): access: AccessEnum = AccessEnum.all display_name: str @@ -117,6 +145,7 @@ class JupyterLabProfile(schema.Base): users: Optional[List[str]] = None groups: Optional[List[str]] = None kubespawner_override: Optional[KubeSpawner] = None + profile_options: Optional[dict[str, ProfileOption]] = None @model_validator(mode="after") def only_yaml_can_have_groups_and_users(self): From 12736ecc6a0d2287df78c76b224b9eff5b05ef06 Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Tue, 4 Feb 2025 22:42:46 -0600 Subject: [PATCH 2/2] support KubeSpawner profile_options --- .../stages/kubernetes_services/__init__.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/_nebari/stages/kubernetes_services/__init__.py b/src/_nebari/stages/kubernetes_services/__init__.py index 8c6190eb1a..0cbde34d69 100644 --- a/src/_nebari/stages/kubernetes_services/__init__.py +++ b/src/_nebari/stages/kubernetes_services/__init__.py @@ -5,7 +5,13 @@ from typing import Any, Dict, List, Optional, Type, Union from urllib.parse import urlencode -from pydantic import ConfigDict, Field, field_validator, model_validator +from pydantic import ( + ConfigDict, + Field, + field_validator, + model_serializer, + model_validator, +) from typing_extensions import Self from _nebari import constants @@ -136,6 +142,14 @@ def validate_choices(cls, v): raise ValueError("Only one choice can be marked as default") return v + # We need to exclude unlisted_choice if not set. + # This was the recommended solution without affecting the parent. + # reference: https://github.com/pydantic/pydantic/discussions/7315 + @model_serializer(mode="wrap") + def serialize_model(self, handler) -> dict[str, Any]: + result = handler(self) + return {k: v for k, v in result.items() if v is not None} + class JupyterLabProfile(schema.Base): access: AccessEnum = AccessEnum.all