Skip to content
Open
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
File renamed without changes.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ To use QScaler-SDK, configure the following environment variables:

### Environment Variables

| Variable | Description |
|--------------------|-----------------------------------------------------------------------------------------------|
| `QWORKER_NAME` | Name of the QWorker CRD to monitor. |
| `PULLING_INTERVAL` | Interval (in seconds) for checking the QWorker CRD status when no tasks are running. |
| `POD_SPEC_HASH` | Hash of the current pod specification. Used to detect changes in the CRD and trigger shutdown.|
| `HOSTNAME` | Name of the pod, automatically set by Kubernetes. |
| Variable | Description |
|--------------------|------------------------------------------------------------------------------------------------|
| `QWORKER_NAME` | Name of the QWorker CRD to monitor. |
| `PULLING_INTERVAL` | Interval (in seconds) for checking the QWorker CRD status when no tasks are running. |
| `POD_SPEC_HASH` | Hash of the current pod specification. Used to detect changes in the CRD and trigger shutdown. |
| `HOSTNAME` | Name of the pod, automatically set by Kubernetes. |
| `KUBECONFIG` | KubeConfig file path, used if running from outside of k8s cluster. |

## Example

Expand Down
5 changes: 2 additions & 3 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ def mock_incluster_environment(request, mocker):
if "incluster" in request.keywords:
os.environ.setdefault("QWORKER_NAME", "fake-qworker")
os.environ.setdefault("HOSTNAME", "localhost")
mocker.patch("qscaler.k8s.k8s_client.K8sClient._load_namespace_from_file", return_value="fake")
mocker.patch("qscaler.k8s.k8s_client.cluster_config.load_incluster_config", return_value=None)

mocker.patch("qscaler.k8s.k8s_client.K8sClient._load_namespace", return_value="fake")
mocker.patch("qscaler.k8s.k8s_client.cluster_config.load_config", return_value=None)


@pytest.fixture(autouse=True)
Expand Down
50 changes: 25 additions & 25 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion qscaler/configuration/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ class Config:
pulling_interval: int = field(default=os.getenv("PULLING_INTERVAL", 2))
pod_name: str = field(default=os.getenv("HOSTNAME")) # in K8s it is the name of the pod
pod_spec_hash: str = field(default=os.getenv("POD_SPEC_HASH"))

kubeconfig: str = field(default=os.getenv("KUBECONFIG"))

config = Config()
20 changes: 13 additions & 7 deletions qscaler/k8s/k8s_client.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import logging
import os
from base64 import b64decode
from typing import Any

from kubernetes import client, config as cluster_config
from kubernetes.client.rest import ApiException

from qscaler.configuration.config import config

Check failure on line 9 in qscaler/k8s/k8s_client.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (F401)

qscaler/k8s/k8s_client.py:9:42: F401 `qscaler.configuration.config.config` imported but unused
from qscaler.utils.singleton import SingletonMeta

logger = logging.getLogger(__name__)
Expand All @@ -14,18 +16,22 @@
class K8sClient(metaclass=SingletonMeta):

def __init__(self):
cluster_config.load_incluster_config()
cluster_config.load_config()
self.api_group = "quickube.com"
self.api_version = "v1alpha1"
self.namespace = self._load_namespace_from_file()
self.namespace = self._load_namespace()

@staticmethod
def _load_namespace_from_file():
try:
with open("/var/run/secrets/kubernetes.io/serviceaccount/namespace", "r") as f:
def _load_namespace() -> str | None:
ns_path = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
if os.path.exists(ns_path):
with open(ns_path) as f:
return f.read().strip()
except FileNotFoundError:
raise RuntimeError("Namespace file not found. Are you running in a Kubernetes cluster?")
try:
contexts, active_context = cluster_config.list_kube_config_contexts()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about loading from env var, with fall back to default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if im getting you right. that might cause problems, if a mistake was entered in the env var and is different then the "/var/run/secrets/kubernetes.io/serviceaccount/namespace". i think default should be the 'incluster' namespace file, then fallback to other methods

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using kubeconfig context namespace used only for development purposes..
if the pod in cluster, the namespace file will exists and all will be just fine.

the only reason to provide namespace will be for development reasons..
I would even delete the fallback to default, and will try to use env var, it not exists, exit the program..
using kubeconfig namespace just too implicit

return active_context["context"]["namespace"]
except (KeyError, StopIteration):
return "default"

def get_qworker(self, name: str) -> dict:
api_instance = client.CustomObjectsApi()
Expand Down
Loading