Skip to content

Commit

Permalink
Merge branch 'develop' into use-jhub-5.0.0b2
Browse files Browse the repository at this point in the history
  • Loading branch information
krassowski authored May 23, 2024
2 parents 77c8a2d + a503572 commit 6829f19
Show file tree
Hide file tree
Showing 24 changed files with 141 additions and 27 deletions.
18 changes: 10 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,21 +136,23 @@ module = [
ignore_missing_imports = true

[tool.ruff]
extend-exclude = [
"src/_nebari/template",
"home",
"__pycache__"
]

[tool.ruff.lint]
select = [
"E",
"F",
"PTH",
"E", # E: pycodestyle rules
"F", # F: pyflakes rules
"PTH", # PTH: flake8-use-pathlib rules
]
ignore = [
"E501", # Line too long
"F821", # Undefined name
"PTH123", # open() should be replaced by Path.open()
]
extend-exclude = [
"src/_nebari/template",
"home",
"__pycache__"
]

[tool.coverage.run]
branch = true
Expand Down
3 changes: 3 additions & 0 deletions src/_nebari/stages/infrastructure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class AzureInputVars(schema.Base):
tags: Dict[str, str] = {}
max_pods: Optional[int] = None
network_profile: Optional[Dict[str, str]] = None
workload_identity_enabled: bool = False


class AWSNodeGroupInputVars(schema.Base):
Expand Down Expand Up @@ -380,6 +381,7 @@ class AzureProvider(schema.Base):
tags: Optional[Dict[str, str]] = {}
network_profile: Optional[Dict[str, str]] = None
max_pods: Optional[int] = None
workload_identity_enabled: bool = False

@model_validator(mode="before")
@classmethod
Expand Down Expand Up @@ -781,6 +783,7 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]):
tags=self.config.azure.tags,
network_profile=self.config.azure.network_profile,
max_pods=self.config.azure.max_pods,
workload_identity_enabled=self.config.azure.workload_identity_enabled,
).model_dump()
elif self.config.provider == schema.ProviderEnum.aws:
return AWSInputVars(
Expand Down
5 changes: 3 additions & 2 deletions src/_nebari/stages/infrastructure/template/azure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module "kubernetes" {
max_size = config.max_nodes
}
]
vnet_subnet_id = var.vnet_subnet_id
private_cluster_enabled = var.private_cluster_enabled
vnet_subnet_id = var.vnet_subnet_id
private_cluster_enabled = var.private_cluster_enabled
workload_identity_enabled = var.workload_identity_enabled
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ resource "azurerm_kubernetes_cluster" "main" {
resource_group_name = var.resource_group_name
tags = var.tags

# To enable Azure AD Workload Identity oidc_issuer_enabled must be set to true.
oidc_issuer_enabled = var.workload_identity_enabled
workload_identity_enabled = var.workload_identity_enabled

# DNS prefix specified when creating the managed cluster. Changing this forces a new resource to be created.
dns_prefix = "Nebari" # required

Expand Down Expand Up @@ -39,6 +43,9 @@ resource "azurerm_kubernetes_cluster" "main" {
"azure-node-pool" = var.node_groups[0].name
}
tags = var.tags

# temparory_name_for_rotation must be <= 12 characters
temporary_name_for_rotation = "${substr(var.node_groups[0].name, 0, 9)}tmp"
}

sku_tier = "Free" # "Free" [Default] or "Paid"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@ output "kubeconfig" {
sensitive = true
value = azurerm_kubernetes_cluster.main.kube_config_raw
}

output "cluster_oidc_issuer_url" {
description = "The OpenID Connect issuer URL that is associated with the AKS cluster"
value = azurerm_kubernetes_cluster.main.oidc_issuer_url
}

output "resource_group_name" {
description = "The name of the resource group in which the AKS cluster is created"
value = azurerm_kubernetes_cluster.main.resource_group_name
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,9 @@ variable "max_pods" {
type = number
default = 60
}

variable "workload_identity_enabled" {
description = "Enable Workload Identity"
type = bool
default = false
}
10 changes: 10 additions & 0 deletions src/_nebari/stages/infrastructure/template/azure/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,13 @@ output "kubeconfig_filename" {
description = "filename for nebari kubeconfig"
value = var.kubeconfig_filename
}

output "cluster_oidc_issuer_url" {
description = "The OpenID Connect issuer URL that is associated with the AKS cluster"
value = module.kubernetes.cluster_oidc_issuer_url
}

output "resource_group_name" {
description = "The name of the resource group in which the AKS cluster is created"
value = module.kubernetes.resource_group_name
}
6 changes: 6 additions & 0 deletions src/_nebari/stages/infrastructure/template/azure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,9 @@ variable "max_pods" {
type = number
default = 60
}

variable "workload_identity_enabled" {
description = "Enable Workload Identity"
type = bool
default = false
}
5 changes: 5 additions & 0 deletions src/_nebari/stages/kubernetes_services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
TIMEOUT = 10 # seconds


_forwardauth_middleware_name = "traefik-forward-auth"


@schema.yaml_object(schema.yaml)
class AccessEnum(str, enum.Enum):
all = "all"
Expand Down Expand Up @@ -327,6 +330,7 @@ class KubernetesServicesInputVars(schema.Base):
realm_id: str
node_groups: Dict[str, Dict[str, str]]
jupyterhub_logout_redirect_url: str = Field(alias="jupyterhub-logout-redirect-url")
forwardauth_middleware_name: str = _forwardauth_middleware_name


def _split_docker_image_name(image_name):
Expand Down Expand Up @@ -383,6 +387,7 @@ class DaskGatewayInputVars(schema.Base):
dask_worker_image: ImageNameTag = Field(alias="dask-worker-image")
dask_gateway_profiles: Dict[str, Any] = Field(alias="dask-gateway-profiles")
cloud_provider: str = Field(alias="cloud-provider")
forwardauth_middleware_name: str = _forwardauth_middleware_name


class MonitoringInputVars(schema.Base):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ module "dask-gateway" {
profiles = var.dask-gateway-profiles

cloud-provider = var.cloud-provider

forwardauth_middleware_name = var.forwardauth_middleware_name
}
18 changes: 17 additions & 1 deletion src/_nebari/stages/kubernetes_services/template/forward-auth.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,21 @@ module "forwardauth" {
external-url = var.endpoint
realm_id = var.realm_id

node-group = var.node_groups.general
node-group = var.node_groups.general
forwardauth_middleware_name = var.forwardauth_middleware_name
}

variable "forwardauth_middleware_name" {
description = "Name of the traefik forward auth middleware"
type = string
}

output "forward-auth-middleware" {
description = "middleware name for use with forward auth"
value = module.forwardauth.forward-auth-middleware
}

output "forward-auth-service" {
description = "middleware name for use with forward auth"
value = module.forwardauth.forward-auth-service
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ resource "kubernetes_manifest" "forwardauth-middleware" {
apiVersion = "traefik.containo.us/v1alpha1"
kind = "Middleware"
metadata = {
name = "traefik-forward-auth"
name = var.forwardauth_middleware_name
namespace = var.namespace
}
spec = {
forwardAuth = {
address = "http://forwardauth-service:4181"
address = "http://${kubernetes_service.forwardauth-service.metadata.0.name}:4181"
authResponseHeaders = [
"X-Forwarded-User"
]
Expand All @@ -175,7 +175,7 @@ resource "kubernetes_manifest" "forwardauth-ingressroute" {

middlewares = [
{
name = "traefik-forward-auth"
name = kubernetes_manifest.forwardauth-middleware.manifest.metadata.name
namespace = var.namespace
}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
output "forward-auth-middleware" {
description = "middleware name for use with forward auth"
value = {
name = kubernetes_manifest.forwardauth-middleware.manifest.metadata.name
}
}

output "forward-auth-service" {
description = "middleware name for use with forward auth"
value = {
name = kubernetes_service.forwardauth-service.metadata.0.name
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ variable "node-group" {
value = string
})
}

variable "forwardauth_middleware_name" {
description = "Name of the traefik forward auth middleware"
type = string
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ resource "kubernetes_manifest" "chain-middleware" {
chain = {
middlewares = [
{
name = "traefik-forward-auth"
name = var.forwardauth_middleware_name
namespace = var.namespace
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,7 @@ variable "cloud-provider" {
description = "Name of the cloud provider to deploy to."
type = string
}

variable "forwardauth_middleware_name" {
type = string
}
3 changes: 3 additions & 0 deletions src/_nebari/stages/nebari_tf_extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]):
"stages/05-kubernetes-keycloak"
]["keycloak_nebari_bot_password"]["value"],
"helm_extensions": [_.model_dump() for _ in self.config.helm_extensions],
"forwardauth_middleware_name": stage_outputs[
"stages/07-kubernetes-services"
]["forward-auth-middleware"]["value"]["name"],
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
locals {
middlewares = (var.private) ? ([{
name = "traefik-forward-auth"
name = var.forwardauth_middleware_name
namespace = var.namespace
}]) : ([])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,8 @@ variable "keycloak_nebari_bot_password" {
type = string
default = ""
}

variable "forwardauth_middleware_name" {
description = "Name of the traefik forward auth middleware"
type = string
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module "extension" {
nebari-realm-id = var.realm_id

keycloak_nebari_bot_password = each.value.keycloakadmin ? var.keycloak_nebari_bot_password : ""
forwardauth_middleware_name = var.forwardauth_middleware_name

envs = lookup(each.value, "envs", [])
}
5 changes: 5 additions & 0 deletions src/_nebari/stages/nebari_tf_extensions/template/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@ variable "helm_extensions" {
variable "keycloak_nebari_bot_password" {
description = "Keycloak password for nebari-bot"
}

variable "forwardauth_middleware_name" {
description = "Name of the traefik forward auth middleware"
type = string
}
29 changes: 19 additions & 10 deletions tests/common/navigator.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def reset_workspace(self):
self._set_environment_via_popup(kernel=None)

# go to Kernel menu
kernel_menuitem = self.page.get_by_text("Kernel", exact=True)
kernel_menuitem = self.page.get_by_role("menuitem", name="Kernel", exact=True)
kernel_menuitem.click()
# shut down multiple running kernels
with contextlib.suppress(Exception):
Expand Down Expand Up @@ -320,14 +320,23 @@ def _set_environment_via_popup(self, kernel=None):
# failure here indicates that the environment doesn't exist either
# because of incorrect naming syntax or because the env is still
# being built
self.page.get_by_role("combobox").nth(1).select_option(kernel)
# click Select to close popup (deal with the two formats of this dialog)
try:
self.page.get_by_role("button", name="Select Kernel").click()
except Exception:
self.page.locator("div").filter(has_text="No KernelSelect").get_by_role(
"button", name="Select Kernel"
).click()

new_launcher_popup = self.page.locator(
".jp-KernelSelector-Dialog .jp-NewLauncher-table table"
).nth(0)
if new_launcher_popup.is_visible():
# for when the jupyterlab-new-launcher extension is installed
new_launcher_popup.locator("td").nth(0).click()
else:
# for when only the native launcher is available
self.page.get_by_role("combobox").nth(1).select_option(kernel)
# click Select to close popup (deal with the two formats of this dialog)
try:
self.page.get_by_role("button", name="Select Kernel").click()
except Exception:
self.page.locator("div").filter(
has_text="No KernelSelect"
).get_by_role("button", name="Select Kernel").click()

def set_environment(self, kernel):
"""Set environment of a jupyter notebook.
Expand All @@ -350,7 +359,7 @@ def set_environment(self, kernel):
popup = self._check_for_kernel_popup()
# if there is not a kernel popup, make it appear
if not popup:
self.page.get_by_text("Kernel", exact=True).click()
self.page.get_by_role("menuitem", name="Kernel", exact=True).click()
self.page.get_by_role("menuitem", name="Change Kernel…").get_by_text(
"Change Kernel…"
).click()
Expand Down
2 changes: 1 addition & 1 deletion tests/common/run_notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def _get_outputs(self) -> List[str]:

def _restart_run_all(self):
# restart run all cells
self.nav.page.get_by_text("Kernel", exact=True).click()
self.nav.page.get_by_role("menuitem", name="Kernel", exact=True).click()
self.nav.page.get_by_role(
"menuitem", name="Restart Kernel and Run All Cells…"
).get_by_text("Restart Kernel and Run All Cells…").click()
Expand Down
1 change: 1 addition & 0 deletions tests/tests_deployment/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def get_jupyterhub_token(note="jupyterhub-tests-deployment"):
f"https://{constants.NEBARI_HOSTNAME}/hub/api/users/{constants.KEYCLOAK_USERNAME}/tokens",
headers=headers,
json=data,
verify=False,
)

return r.json()["token"]
Expand Down

0 comments on commit 6829f19

Please sign in to comment.