nebari-dev / nebari

🪴 Nebari - your open source data science platform
https://nebari.dev
BSD 3-Clause "New" or "Revised" License
279 stars 92 forks source link

[BUG] - `IndexError` during initialization with DigitalOcean provider #2810

Open viniciusdc opened 1 day ago

viniciusdc commented 1 day ago

Describe the bug

When initializing Nebari with DigitalOcean as the cloud provider, an IndexError: list index out of range is encountered. https://github.com/nebari-dev/nebari/actions/runs/11601903835/job/32305692369?pr=2795

https://github.com/nebari-dev/nebari/blob/88dfe2447c56689c3a9828e6abef4e1c02903faf/src/_nebari/provider/cloud/digital_ocean.py#L62-L70

This occurs because the HIGHEST_SUPPORTED_K8S_VERSION https://github.com/nebari-dev/nebari/blob/88dfe2447c56689c3a9828e6abef4e1c02903faf/src/_nebari/constants.py#L15

Which is lower than the Kubernetes versions currently supported by DigitalOcean (['1.29.9', '1.30.5', '1.31.1']). As a result, the version filtering logic fails, leading to an empty list and subsequent error.

Expected behavior

Init shoudl work as expected, and also, the error message should be more descriptive

OS and architecture in which you are running Nebari

Linux (CI)

How to Reproduce the problem?

Here's a code snippet you can use to reproduce the error:

import os
import requests
import typing
import re

from _nebari.constants import HIGHEST_SUPPORTED_K8S_VERSION

DIGITALOCEAN_TOKEN = (
    "dop_v1_**********"
)

def digital_ocean_request(url, method="GET", json=None):
    BASE_DIGITALOCEAN_URL = "https://api.digitalocean.com/v2/"

    headers = {"Authorization": f"Bearer {DIGITALOCEAN_TOKEN}"}

    method_map = {
        "GET": requests.get,
        "DELETE": requests.delete,
    }

    response = method_map[method](
        f"{BASE_DIGITALOCEAN_URL}{url}", headers=headers, json=json
    )
    response.raise_for_status()

    return response

def _kubernetes_options():
    return digital_ocean_request("kubernetes/options").json()

def filter_by_highest_supported_k8s_version(k8s_versions_list):
    filtered_k8s_versions_list = []
    for k8s_version in k8s_versions_list:
        version = tuple(
            filter(None, re.search(r"(\d+)\.(\d+)(?:\.(\d+))?", k8s_version).groups())
        )
        print(f"version: {version}")
        if version <= HIGHEST_SUPPORTED_K8S_VERSION:
            print(
                f"version: {version} is less than or equal to HIGHEST_SUPPORTED_K8S_VERSION: {HIGHEST_SUPPORTED_K8S_VERSION}"
            )
            filtered_k8s_versions_list.append(k8s_version)
    return filtered_k8s_versions_list

def kubernetes_versions() -> typing.List[str]:
    """Return list of available kubernetes supported by cloud provider. Sorted from oldest to latest."""
    supported_kubernetes_versions = sorted(
        [_["slug"].split("-")[0] for _ in _kubernetes_options()["options"]["versions"]]
    )
    print(f"supported_kubernetes_versions: {supported_kubernetes_versions}")
    filtered_versions = filter_by_highest_supported_k8s_version(
        supported_kubernetes_versions
    )
    print(f"filtered_versions: {filtered_versions}")
    return [f"{v}-do.0" for v in filtered_versions]

if __name__ == "__main__":
    versions = kubernetes_versions()
    print(f"versions: {versions}")
    sorted(versions)[-1]

Command output

No response

Versions and dependencies used.

No response

Compute environment

Digital Ocean

Integrations

No response

Anything else?

No response

viniciusdc commented 1 day ago

I am not entirely sure if this should be worked on, though, since we will be decommissioning DO soon. Addressing this requires a global update in the supported Kubertnes version across all providers. If we are happy with bumping it for all, then it becomes pretty straightforward to fix.