Skip to content

Commit d4f2654

Browse files
authored
chore: re-enable notebooks tests and use kind (#1070)
1 parent 5887e01 commit d4f2654

File tree

11 files changed

+207
-244
lines changed

11 files changed

+207
-244
lines changed

.devcontainer/devcontainer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"ghcr.io/EliiseS/devcontainer-features/bash-profile:1": {
3030
"command": "alias k=kubectl"
3131
},
32-
"./k3d": {},
32+
"ghcr.io/devcontainers-extra/features/kind:1": {
33+
"version": "v0.30.0"
34+
},
3335
"ghcr.io/devcontainers/features/go:1": {},
3436
"./rclone": {
3537
"rclone_repository": "https://github.com/SwissDataScienceCenter/rclone.git",

.devcontainer/k3d/devcontainer-feature.json

Lines changed: 0 additions & 17 deletions
This file was deleted.

.devcontainer/k3d/install.sh

Lines changed: 0 additions & 14 deletions
This file was deleted.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ result
8484

8585
# tests
8686
.k3d-config.yaml
87+
.kind-kubeconfig.yaml
8788

8889
# Misc
8990
*.pem

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ test_setup: ## Prep for the tests - removes old coverage reports if one is pres
7777
main_tests: ## Run the main (i.e. non-schemathesis tests)
7878
DUMMY_STORES=true poetry run alembic --name common upgrade heads
7979
poetry run alembic --name common check
80-
poetry run pytest -m "not schemathesis" -n auto -v
80+
poetry run pytest -m "not schemathesis" -n auto -v --dist loadgroup
8181

8282
.PHONY: schemathesis_tests
8383
schemathesis_tests: ## Run schemathesis checks

components/renku_data_services/k8s/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class KubeConfigEnv(KubeConfig):
6767
"""Get a kube config from the environment."""
6868

6969
def __init__(self) -> None:
70-
super().__init__(ns=os.environ.get("K8S_NAMESPACE", "default"))
70+
super().__init__(ns=os.environ.get("K8S_NAMESPACE", "default"), kubeconfig=os.environ.get("KUBECONFIG"))
7171

7272

7373
async def from_kubeconfig_file(kubeconfig_path: str) -> KubeConfig:

components/renku_data_services/k8s/watcher/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ async def collect_metrics(
189189
await metrics.session_stopped(user=user, metadata={"session_id": new_obj.meta.name})
190190
return
191191
previous_state = previous_obj.manifest.get("status", {}).get("state", None) if previous_obj else None
192-
match new_obj.obj.status.state:
192+
match new_obj.obj.status.get("state"):
193193
case State.Running.value if previous_state is None or previous_state == State.NotReady.value:
194194
# session starting
195195
resource_class_id = int(new_obj.obj.metadata.annotations.get("renku.io/resource_class_id"))

test/bases/renku_data_services/data_api/conftest.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import asyncio
2+
import contextlib
13
import json
24
from collections.abc import AsyncGenerator, Callable
35
from copy import deepcopy
6+
from datetime import timedelta
7+
from pathlib import Path
48
from typing import Any, Protocol
59

610
import pytest
@@ -19,9 +23,14 @@
1923
from renku_data_services.data_api.app import register_all_handlers
2024
from renku_data_services.data_api.dependencies import DependencyManager
2125
from renku_data_services.data_connectors.apispec import DataConnector as ApiDataConnector
26+
from renku_data_services.k8s.clients import K8sClusterClient
27+
from renku_data_services.k8s.config import from_kubeconfig_file, get_clusters
28+
from renku_data_services.k8s.constants import ClusterId
29+
from renku_data_services.k8s.watcher import K8sWatcher, k8s_object_handler
2230
from renku_data_services.migrations.core import run_migrations_for_app
2331
from renku_data_services.namespace.apispec import GroupResponse as ApiGroup
2432
from renku_data_services.namespace.models import UserNamespace
33+
from renku_data_services.notebooks.constants import JUPYTER_SESSION_GVK
2534
from renku_data_services.project.apispec import Project as ApiProject
2635
from renku_data_services.search.apispec import SearchResult
2736
from renku_data_services.secrets_storage_api.app import register_all_handlers as register_secrets_handlers
@@ -33,6 +42,7 @@
3342
from renku_data_services.users.dummy_kc_api import DummyKeycloakAPI
3443
from renku_data_services.users.models import UserInfo
3544
from renku_data_services.utils.middleware import validate_null_byte
45+
from test.bases.renku_data_services.data_api.utils import KindCluster, setup_amalthea
3646
from test.bases.renku_data_services.data_tasks.test_sync import get_kc_users
3747
from test.utils import SanicReusableASGITestClient, TestDependencyManager
3848

@@ -727,3 +737,52 @@ def __make_headers(user: UserInfo, admin: bool = False) -> dict[str, str]:
727737
}
728738
)
729739
return {"Authorization": f"Bearer {access_token}"}
740+
741+
742+
@pytest.fixture(scope="session")
743+
def cluster_name():
744+
return f"k8s-cluster-{str(ULID()).lower()}"
745+
746+
747+
@pytest.fixture(scope="session")
748+
def kubeconfig_path(monkeysession):
749+
kconf = ".kind-kubeconfig.yaml"
750+
monkeysession.setenv("KUBECONFIG", kconf)
751+
return Path(kconf)
752+
753+
754+
@pytest.fixture(scope="session")
755+
def cluster(cluster_name, kubeconfig_path):
756+
with KindCluster(cluster_name, kubeconfig=str(kubeconfig_path)) as cluster:
757+
yield cluster
758+
759+
760+
@pytest.fixture(scope="session")
761+
def amalthea_installation(cluster):
762+
setup_amalthea("amalthea", "amalthea", "0.22.0", cluster)
763+
764+
765+
@pytest_asyncio.fixture
766+
async def jupyter_server_k8s_watcher(cluster, amalthea_installation, app_manager_instance):
767+
app_manager = app_manager_instance
768+
default_kubeconfig = await from_kubeconfig_file(cluster.kubeconfig)
769+
clusters: dict[ClusterId, K8sClusterClient] = {}
770+
async for client in get_clusters(
771+
kube_conf_root_dir=app_manager.config.k8s_config_root,
772+
default_kubeconfig=default_kubeconfig,
773+
cluster_repo=app_manager.cluster_repo,
774+
):
775+
clusters[client.get_cluster().id] = client
776+
777+
# sleep to give amalthea a chance to create the CRDs, otherwise the watcher can error out
778+
await asyncio.sleep(1)
779+
watcher = K8sWatcher(
780+
handler=k8s_object_handler(app_manager.config.nb_config.k8s_db_cache, app_manager.metrics, app_manager.rp_repo),
781+
clusters=clusters,
782+
kinds=[JUPYTER_SESSION_GVK],
783+
db_cache=app_manager.config.nb_config.k8s_db_cache,
784+
)
785+
asyncio.create_task(watcher.start())
786+
yield
787+
with contextlib.suppress(TimeoutError):
788+
await watcher.stop(timeout=timedelta(seconds=1))

0 commit comments

Comments
 (0)