Skip to content

Commit

Permalink
[Nuclio] Delete remote function config map (#5530)
Browse files Browse the repository at this point in the history
  • Loading branch information
rokatyy committed May 8, 2024
1 parent f747bd0 commit a93ee1d
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
10 changes: 9 additions & 1 deletion server/api/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1353,10 +1353,17 @@ async def delete_function(
project: str,
function: str,
_semaphore: asyncio.Semaphore,
k8s_helper: server.api.utils.singletons.k8s.K8sHelper,
) -> tuple[str, str]:
async with _semaphore:
try:
await nuclio_client.delete_function(name=function, project_name=project)

config_map = k8s_helper.get_configmap(
function, mlrun.common.constants.MLRUN_MODEL_CONF
)
if config_map:
k8s_helper.delete_configmap(config_map.metadata.name)
return None
except Exception as exc:
# return tuple with failure info
Expand All @@ -1370,8 +1377,9 @@ async def delete_function(
failed_requests = []

async with server.api.utils.clients.async_nuclio.Client(auth_info) as client:
k8s_helper = server.api.utils.singletons.k8s.get_k8s_helper()
tasks = [
delete_function(client, project_name, function_name, semaphore)
delete_function(client, project_name, function_name, semaphore, k8s_helper)
for function_name in function_names
]

Expand Down
43 changes: 25 additions & 18 deletions server/api/utils/singletons/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,28 +552,20 @@ def ensure_configmap(
labels: dict = None,
):
namespace = self.resolve_namespace(namespace)

have_confmap = False
label_name = "resource_name"
full_name = f"{resource}-{name}"
name = (
full_name
if len(full_name) <= 63
else full_name[:59] + self._generate_rand_string(4)
)

have_confmap = False
configmaps_with_label = self.v1api.list_namespaced_config_map(
namespace=namespace, label_selector=f"{label_name}={full_name}"
)

if len(configmaps_with_label.items) > 1:
raise mlrun.errors.MLRunInternalServerError(
f"Received more than one config map for label: {full_name}"
)

if len(configmaps_with_label.items) == 1:
name = configmaps_with_label.items[0].metadata.name
configmap_with_label = self.get_configmap(name, resource, namespace)
if configmap_with_label:
name = configmap_with_label.metadata.name
have_confmap = True
else:
name = (
full_name
if len(full_name) <= 63
else full_name[:59] + self._generate_rand_string(4)
)

if labels is None:
labels = {label_name: full_name}
Expand Down Expand Up @@ -610,6 +602,21 @@ def ensure_configmap(
raise exc
return name

@raise_for_status_code
def get_configmap(self, name: str, resource: str, namespace: str = ""):
namespace = self.resolve_namespace(namespace)
label_name = "resource_name"
full_name = f"{resource}-{name}"
configmaps_with_label = self.v1api.list_namespaced_config_map(
namespace=namespace, label_selector=f"{label_name}={full_name}"
)
if len(configmaps_with_label.items) > 1:
raise mlrun.errors.MLRunInternalServerError(
f"Received more than one config map for label: {full_name}"
)

return configmaps_with_label.items[0] if configmaps_with_label.items else None

@raise_for_status_code
def delete_configmap(self, name: str, namespace: str = "", raise_on_error=True):
namespace = self.resolve_namespace(namespace)
Expand Down
32 changes: 32 additions & 0 deletions tests/api/api/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
import json
import unittest.mock
from http import HTTPStatus
from unittest.mock import AsyncMock, MagicMock, patch

import kubernetes.client
import pytest
from deepdiff import DeepDiff
from fastapi.testclient import TestClient
from kubernetes.client.models import V1ConfigMap, V1ObjectMeta
from sqlalchemy.orm import Session

import mlrun
Expand All @@ -35,6 +37,7 @@
from mlrun.common.schemas import SecurityContextEnrichmentModes
from mlrun.utils import logger
from server.api.api.utils import (
_delete_nuclio_functions_in_batches,
_generate_function_and_task_from_submit_run_body,
_mask_v3io_access_key_env_var,
_mask_v3io_volume_credentials,
Expand Down Expand Up @@ -1658,3 +1661,32 @@ def _assert_env_var_from_secret(
)
assert env_var_value.secret_key_ref.name == secret_name
assert env_var_value.secret_key_ref.key == secret_key


@pytest.mark.asyncio
async def test_delete_function_calls_k8s_helper_methods():
function_names = ["function1"]
async_client_mock = AsyncMock()

k8s_helper_mock = MagicMock()
configmap = V1ConfigMap()
configmap.metadata = V1ObjectMeta(name="config-map-1")
k8s_helper_mock.get_configmap.return_value = configmap

with (
patch(
"server.api.utils.clients.async_nuclio.Client",
return_value=async_client_mock,
),
patch(
"server.api.utils.singletons.k8s.get_k8s_helper",
return_value=k8s_helper_mock,
),
):
failed_requests = await _delete_nuclio_functions_in_batches(
{}, "my-project", function_names
)

assert len(failed_requests) == 0
k8s_helper_mock.get_configmap.assert_called_with("function1", "model-conf")
k8s_helper_mock.delete_configmap.assert_called_with("config-map-1")

0 comments on commit a93ee1d

Please sign in to comment.