Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Configuration guides to setup production HTTP server (apache2, nginx and
caddy) on SLES.

### Changed
- agent: Make RacksDB library optional with lazy loading only when enabled in
configuration (#683). Contribution from @faganihajizada.

### Fixed
- agent: Import of ClusterShell NodeSet class (#682). Contribution from
@faganihajizada.
Expand Down
7 changes: 5 additions & 2 deletions slurmweb/apps/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import logging

from rfl.web.tokens import RFLTokenizedRBACWebApp
from racksdb.errors import RacksDBSchemaError, RacksDBFormatError
from racksdb.web.app import RacksDBWebBlueprint

try:
from werkzeug.middleware import dispatcher
Expand Down Expand Up @@ -65,6 +63,11 @@ def __init__(self, seed):
# If enabled, load RacksDB blueprint and fail with error if unable to load
# schema or database.
if self.settings.racksdb.enabled:
# Lazy load RacksDB module to avoid failing on missing optional external
# dependency when feature is actually disabled.
from racksdb.errors import RacksDBSchemaError, RacksDBFormatError
from racksdb.web.app import RacksDBWebBlueprint

try:
self.register_blueprint(
RacksDBWebBlueprint(
Expand Down
5 changes: 4 additions & 1 deletion slurmweb/tests/apps/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
# SPDX-License-Identifier: MIT

import sys
import unittest
from unittest import mock

from slurmweb.errors import SlurmwebConfigurationError

from ..lib.agent import TestAgentBase
from ..lib.agent import TestAgentBase, is_racksdb_available


class TestAgentApp(TestAgentBase):
Expand All @@ -22,6 +23,7 @@ def test_app_loaded(self):
with self.assertNoLogs("slurmweb", level="ERROR"):
self.setup_client()

@unittest.skipIf(not is_racksdb_available(), "RacksDB not installed")
def test_app_racksdb_format_error(self):
with self.assertLogs("slurmweb", level="ERROR") as cm:
self.setup_client(racksdb_format_error=True)
Expand All @@ -33,6 +35,7 @@ def test_app_racksdb_format_error(self):
],
)

@unittest.skipIf(not is_racksdb_available(), "RacksDB not installed")
def test_app_racksdb_schema_error(self):
with self.assertLogs("slurmweb", level="ERROR") as cm:
self.setup_client(racksdb_schema_error=True)
Expand Down
45 changes: 35 additions & 10 deletions slurmweb/tests/lib/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from unittest import mock
import tempfile
import os

from importlib.util import find_spec

import werkzeug
from flask import Blueprint, jsonify
Expand All @@ -18,7 +18,6 @@
from rfl.permissions.rbac import ANONYMOUS_ROLE
from slurmweb.apps import SlurmwebAppSeed
from slurmweb.apps.agent import SlurmwebAppAgent
from racksdb.errors import RacksDBFormatError, RacksDBSchemaError

from .utils import (
mock_slurmrestd_responses,
Expand All @@ -27,6 +26,11 @@
)


def is_racksdb_available():
"""Check if RacksDB is available for testing."""
return find_spec("racksdb") is not None


CONF_TPL = """
[service]
cluster=test
Expand Down Expand Up @@ -147,21 +151,41 @@ def setup_client(
anonymous_enabled=True,
use_token=True,
):
# Check if RacksDB is available for mocking
try:
from racksdb.errors import RacksDBFormatError, RacksDBSchemaError
except ModuleNotFoundError:
# RacksDB not available, disable it in config
racksdb = False

self.setup_agent_conf(
slurmrestd_parameters=slurmrestd_parameters,
racksdb=racksdb,
metrics=metrics,
cache=cache,
)

# Start the app with mocked RacksDB web blueprint
with mock.patch("slurmweb.apps.agent.RacksDBWebBlueprint") as m:
if racksdb_format_error:
m.side_effect = RacksDBFormatError("fake db format error")
elif racksdb_schema_error:
m.side_effect = RacksDBSchemaError("fake db schema error")
else:
m.return_value = FakeRacksDBWebBlueprint()
if racksdb:
# RacksDB is available, start app with mocked RacksDB web blueprint
with mock.patch("racksdb.web.app.RacksDBWebBlueprint") as m:
if racksdb_format_error:
m.side_effect = RacksDBFormatError("fake db format error")
elif racksdb_schema_error:
m.side_effect = RacksDBSchemaError("fake db schema error")
else:
m.return_value = FakeRacksDBWebBlueprint()
self.app = SlurmwebAppAgent(
SlurmwebAppSeed.with_parameters(
debug=False,
log_flags=["ALL"],
log_component=None,
debug_flags=[],
conf_defs=self.conf_defs,
conf=self.conf.name,
)
)
else:
# RacksDB disabled in config or not available
self.app = SlurmwebAppAgent(
SlurmwebAppSeed.with_parameters(
debug=False,
Expand All @@ -172,6 +196,7 @@ def setup_client(
conf=self.conf.name,
)
)

if not anonymous_enabled:
self.app.policy.disable_anonymous()

Expand Down
4 changes: 3 additions & 1 deletion slurmweb/tests/lib/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@
import jinja2

from rfl.authentication.user import AuthenticatedUser, AnonymousUser
from racksdb.version import get_version as racksdb_get_version

from slurmweb.version import get_version
from slurmweb.apps import SlurmwebAppSeed
from slurmweb.apps.gateway import SlurmwebAppGateway
from slurmweb.apps.gateway import SlurmwebAgent, SlurmwebAgentRacksDBSettings
from slurmweb.views.agent import racksdb_get_version

from .utils import SlurmwebCustomTestResponse


CONF_TPL = """
[agents]
url=http://localhost
Expand Down
3 changes: 1 addition & 2 deletions slurmweb/tests/views/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@

from ClusterShell.NodeSet import NodeSet

from racksdb.version import get_version as racksdb_get_version

from slurmweb.version import get_version
from slurmweb.slurmrestd.errors import (
SlurmrestConnectionError,
SlurmrestdInvalidResponseError,
)
from slurmweb.cache import CachingService
from slurmweb.views.agent import racksdb_get_version

from ..lib.agent import TestAgentBase
from ..lib.utils import all_slurm_api_versions, flask_404_description
Expand Down
5 changes: 4 additions & 1 deletion slurmweb/tests/views/test_agent_racksdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#
# SPDX-License-Identifier: MIT

import unittest

from ..lib.agent import TestAgentBase
from ..lib.agent import TestAgentBase, is_racksdb_available
from ..lib.utils import flask_404_description


@unittest.skipIf(not is_racksdb_available(), "RacksDB not installed")
class TestAgentRacksDBEnabledRequest(TestAgentBase):
def setUp(self):
self.setup_client()
Expand All @@ -23,6 +25,7 @@ def test_request_racksdb(self):
)


@unittest.skipIf(not is_racksdb_available(), "RacksDB not installed")
class TestAgentRacksDBUnabledRequest(TestAgentBase):
def setUp(self):
self.setup_client(racksdb_format_error=True)
Expand Down
11 changes: 10 additions & 1 deletion slurmweb/views/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from flask import Response, current_app, jsonify, abort, request
from rfl.web.tokens import rbac_action, check_jwt
from racksdb.version import get_version as racksdb_get_version

from ..version import get_version
from ..errors import SlurmwebCacheError, SlurmwebMetricsDBError
Expand All @@ -26,6 +25,16 @@
logger = logging.getLogger(__name__)


def racksdb_get_version():
"""Get RacksDB version if available, or return 'N/A' if not installed."""
try:
from racksdb.version import get_version

return get_version()
except ModuleNotFoundError:
return "N/A (not installed)"


def version():
return Response(f"Slurm-web agent v{get_version()}\n", mimetype="text/plain")

Expand Down