Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion docs/docs/reference/server/config.yml.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,28 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
show_root_heading: false

??? info "Specifying `data`"
To specify kubeconfig contents directly via `data`, convert it to a string:
To specify kubeconfig contents directly via `data`, you can convert it to a string:

```shell
yq -o=json ~/.kube/config | jq -c | jq -R
```

or copy kubeconfig contents under `data` as-is:

```yaml
type: kubernetes
kubeconfig:
data:
apiVersion: v1
clusters:
- cluster:
# ...
contexts:
- context:
# ...
# ...
```

###### `projects[n].backends[type=kubernetes].proxy_jump` { #kubernetes-proxy_jump data-toc-label="proxy_jump" }

#SCHEMA# dstack._internal.core.backends.kubernetes.models.KubernetesProxyJumpConfig
Expand Down
4 changes: 2 additions & 2 deletions src/dstack/_internal/core/backends/kubernetes/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
)
from dstack._internal.core.backends.kubernetes.utils import (
call_api_method,
get_api_from_config_data,
get_api_from_config_dict,
get_cluster_public_ip,
get_value,
)
Expand Down Expand Up @@ -99,7 +99,7 @@ def __init__(self, config: KubernetesConfig):
if proxy_jump is None:
proxy_jump = KubernetesProxyJumpConfig()
self.proxy_jump = proxy_jump
self.api = get_api_from_config_data(config.kubeconfig.data)
self.api = get_api_from_config_dict(config.kubeconfig.data)

def get_offers_by_requirements(
self, requirements: Requirements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def validate_config(
self, config: KubernetesBackendConfigWithCreds, default_creds_enabled: bool
):
try:
api = kubernetes_utils.get_api_from_config_data(config.kubeconfig.data)
api = kubernetes_utils.get_api_from_config_dict(config.kubeconfig.data)
api.list_node()
except Exception as e:
logger.debug("Invalid kubeconfig: %s", str(e))
Expand Down
16 changes: 12 additions & 4 deletions src/dstack/_internal/core/backends/kubernetes/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Annotated, Literal, Optional, Union

from pydantic import Field, root_validator
import yaml
from pydantic import Field, root_validator, validator

from dstack._internal.core.backends.base.models import fill_data
from dstack._internal.core.models.common import CoreModel
Expand All @@ -19,7 +20,13 @@ class KubernetesProxyJumpConfig(CoreModel):

class KubeconfigConfig(CoreModel):
filename: Annotated[str, Field(description="The path to the kubeconfig file")] = ""
data: Annotated[str, Field(description="The contents of the kubeconfig file")]
data: Annotated[dict, Field(description="The contents of the kubeconfig file")]

@validator("data", pre=True)
def convert_data(cls, v: Union[str, dict]) -> dict:
if isinstance(v, dict):
return v
return yaml.load(v, yaml.FullLoader)


class KubernetesBackendConfig(CoreModel):
Expand All @@ -39,10 +46,11 @@ class KubernetesBackendConfigWithCreds(KubernetesBackendConfig):
class KubeconfigFileConfig(CoreModel):
filename: Annotated[str, Field(description="The path to the kubeconfig file")]
data: Annotated[
Optional[str],
# str data converted to dict when parsed as KubeconfigConfig
Optional[Union[str, dict]],
Field(
description=(
"The contents of the kubeconfig file."
"The contents of the kubeconfig file specified as yaml or a string."
" When configuring via `server/config.yml`, it's automatically filled from `filename`."
" When configuring via UI, it has to be specified explicitly"
)
Expand Down
6 changes: 0 additions & 6 deletions src/dstack/_internal/core/backends/kubernetes/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ast
from typing import Any, Callable, List, Literal, Optional, TypeVar, Union, get_origin, overload

import yaml
from kubernetes import client as kubernetes_client
from kubernetes import config as kubernetes_config
from typing_extensions import ParamSpec
Expand All @@ -10,11 +9,6 @@
P = ParamSpec("P")


def get_api_from_config_data(kubeconfig_data: str) -> kubernetes_client.CoreV1Api:
config_dict = yaml.load(kubeconfig_data, yaml.FullLoader)
return get_api_from_config_dict(config_dict)


def get_api_from_config_dict(kubeconfig: dict) -> kubernetes_client.CoreV1Api:
api_client = kubernetes_config.new_client_from_config_dict(config_dict=kubeconfig)
return kubernetes_client.CoreV1Api(api_client=api_client)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
class TestKubernetesConfigurator:
def test_validate_config_valid(self):
config = KubernetesBackendConfigWithCreds(
kubeconfig=KubeconfigConfig(data="valid", filename="-"),
kubeconfig=KubeconfigConfig(data={"config": "valid"}, filename="-"),
proxy_jump=KubernetesProxyJumpConfig(hostname=None, port=None),
)
with patch(
"dstack._internal.core.backends.kubernetes.utils.get_api_from_config_data"
"dstack._internal.core.backends.kubernetes.utils.get_api_from_config_dict"
) as get_api_mock:
api_mock = Mock()
api_mock.list_node.return_value = Mock()
Expand All @@ -29,12 +29,12 @@ def test_validate_config_valid(self):

def test_validate_config_invalid_config(self):
config = KubernetesBackendConfigWithCreds(
kubeconfig=KubeconfigConfig(data="invalid", filename="-"),
kubeconfig=KubeconfigConfig(data={"config": "invalid"}, filename="-"),
proxy_jump=KubernetesProxyJumpConfig(hostname=None, port=None),
)
with (
patch(
"dstack._internal.core.backends.kubernetes.utils.get_api_from_config_data"
"dstack._internal.core.backends.kubernetes.utils.get_api_from_config_dict"
) as get_api_mock,
pytest.raises(BackendInvalidCredentialsError) as exc_info,
):
Expand Down