Skip to content

Commit ecaa94b

Browse files
authored
Retrieve all conda-store environments (#2910)
2 parents f4ca133 + a8af8db commit ecaa94b

File tree

1 file changed

+56
-9
lines changed
  • src/_nebari/stages/kubernetes_services/template/modules/kubernetes/services/jupyterhub/files/jupyterhub

1 file changed

+56
-9
lines changed

src/_nebari/stages/kubernetes_services/template/modules/kubernetes/services/jupyterhub/files/jupyterhub/02-spawner.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
from kubespawner import KubeSpawner # noqa: E402
1212

13+
# conda-store default page size
14+
DEFAULT_PAGE_SIZE_LIMIT = 100
15+
1316

1417
@gen.coroutine
1518
def get_username_hook(spawner):
@@ -23,25 +26,69 @@ def get_username_hook(spawner):
2326
)
2427

2528

29+
def get_total_records(url: str, token: str) -> int:
30+
import urllib3
31+
32+
http = urllib3.PoolManager()
33+
response = http.request("GET", url, headers={"Authorization": f"Bearer {token}"})
34+
decoded_response = json.loads(response.data.decode("UTF-8"))
35+
return decoded_response.get("count", 0)
36+
37+
38+
def generate_paged_urls(base_url: str, total_records: int, page_size: int) -> list[str]:
39+
import math
40+
41+
urls = []
42+
# pages starts at 1
43+
for page in range(1, math.ceil(total_records / page_size) + 1):
44+
urls.append(f"{base_url}?size={page_size}&page={page}")
45+
46+
return urls
47+
48+
49+
# TODO: this should get unit tests. Currently, since this is not a python module,
50+
# adding tests in a traditional sense is not possible. See https://github.com/soapy1/nebari/tree/try-unit-test-spawner
51+
# for a demo on one approach to adding test.
2652
def get_conda_store_environments(user_info: dict):
53+
import os
54+
2755
import urllib3
28-
import yarl
56+
57+
# Check for the environment variable `CONDA_STORE_API_PAGE_SIZE_LIMIT`. Fall
58+
# back to using the default page size limit if not set.
59+
page_size = os.environ.get(
60+
"CONDA_STORE_API_PAGE_SIZE_LIMIT", DEFAULT_PAGE_SIZE_LIMIT
61+
)
2962

3063
external_url = z2jh.get_config("custom.conda-store-service-name")
3164
token = z2jh.get_config("custom.conda-store-jhub-apps-token")
3265
endpoint = "conda-store/api/v1/environment"
3366

34-
url = yarl.URL(f"http://{external_url}/{endpoint}/")
35-
67+
base_url = f"http://{external_url}/{endpoint}/"
3668
http = urllib3.PoolManager()
37-
response = http.request(
38-
"GET", str(url), headers={"Authorization": f"Bearer {token}"}
39-
)
4069

41-
# parse response
42-
j = json.loads(response.data.decode("UTF-8"))
70+
# get total number of records from the endpoint
71+
total_records = get_total_records(base_url, token)
72+
73+
# will contain all the environment info returned from the api
74+
env_data = []
75+
76+
# If there are more records than the specified size limit, then
77+
# will need to page through to get all the available envs
78+
if total_records > page_size:
79+
# generate a list of urls to hit to build the response
80+
urls = generate_paged_urls(base_url, total_records, page_size)
81+
82+
# get content from urls
83+
for url in urls:
84+
response = http.request(
85+
"GET", url, headers={"Authorization": f"Bearer {token}"}
86+
)
87+
decoded_response = json.loads(response.data.decode("UTF-8"))
88+
env_data += decoded_response.get("data", [])
89+
4390
# Filter and return conda environments for the user
44-
return [f"{env['namespace']['name']}-{env['name']}" for env in j.get("data", [])]
91+
return [f"{env['namespace']['name']}-{env['name']}" for env in env_data]
4592

4693

4794
c.Spawner.pre_spawn_hook = get_username_hook

0 commit comments

Comments
 (0)