Skip to content

Commit

Permalink
new: get/put /_admin/server/mode (#312)
Browse files Browse the repository at this point in the history
* new: `mode()`, `server_mode()`, `set_mode()`

* fix: test

getting read-only errors even after running `sys_db.set_mode("default")`. Probably sync issues..
  • Loading branch information
aMahanna authored Jan 26, 2024
1 parent 6653c1b commit 70548d9
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 3 deletions.
22 changes: 22 additions & 0 deletions arango/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
ClusterServerCountError,
ClusterServerEngineError,
ClusterServerIDError,
ClusterServerModeError,
ClusterServerRoleError,
ClusterServerStatisticsError,
ClusterServerVersionError,
Expand Down Expand Up @@ -57,6 +58,27 @@ def response_handler(resp: Response) -> str:

return self._execute(request, response_handler)

def server_mode(self) -> Result[str]:
"""Return the server mode.
In a read-only server, all write operations will fail
with an error code of 1004 (ERROR_READ_ONLY). Creating or dropping
databases and collections will also fail with error code 11 (ERROR_FORBIDDEN).
:return: Server mode. Possible values are "default" or "readonly".
:rtype: str
:raise arango.exceptions.ClusterServerModeError: If retrieval fails.
"""
request = Request(method="get", endpoint="/_admin/server/mode")

def response_handler(resp: Response) -> str:
if resp.is_success:
return str(resp.body["mode"])

raise ClusterServerModeError(resp, request)

return self._execute(request, response_handler)

def server_version(self, server_id: str) -> Result[Json]:
"""Return the version of the given server.
Expand Down
52 changes: 52 additions & 0 deletions arango/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
ServerLogSettingError,
ServerLogSettingSetError,
ServerMetricsError,
ServerModeError,
ServerModeSetError,
ServerReadLogError,
ServerReloadRoutingError,
ServerRequiredDBVersionError,
Expand Down Expand Up @@ -554,6 +556,56 @@ def response_handler(resp: Response) -> str:

return self._execute(request, response_handler)

def mode(self) -> Result[str]:
"""Return the server mode (default or read-only)
In a read-only server, all write operations will fail
with an error code of 1004 (ERROR_READ_ONLY). Creating or dropping
databases and collections will also fail with error code 11 (ERROR_FORBIDDEN).
:return: Server mode. Possible values are "default" or "readonly".
:rtype: str
:raise arango.exceptions.ServerModeError: If retrieval fails.
"""
request = Request(method="get", endpoint="/_admin/server/mode")

def response_handler(resp: Response) -> str:
if resp.is_success:
return str(resp.body["mode"])

raise ServerModeError(resp, request)

return self._execute(request, response_handler)

def set_mode(self, mode: str) -> Result[Json]:
"""Set the server mode to read-only or default.
Update mode information about a server. The JSON response will
contain a field mode with the value readonly or default.
In a read-only server all write operations will fail with an error
code of 1004 (ERROR_READ_ONLY). Creating or dropping of databases
and collections will also fail with error code 11 (ERROR_FORBIDDEN).
This is a protected API. It requires authentication and administrative
server rights.
:param mode: Server mode. Possible values are "default" or "readonly".
:type mode: str
:return: Server mode.
:rtype: str
:raise arango.exceptions.ServerModeSetError: If set fails.
"""
request = Request(
method="put", endpoint="/_admin/server/mode", data={"mode": mode}
)

def response_handler(resp: Response) -> Json:
if resp.is_success:
return format_body(resp.body)
raise ServerModeSetError(resp, request)

return self._execute(request, response_handler)

def time(self) -> Result[datetime]:
"""Return server system time.
Expand Down
16 changes: 14 additions & 2 deletions arango/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,15 @@ class ServerMetricsError(ArangoServerError):


class ServerRoleError(ArangoServerError):
"""Failed to retrieve server role in a cluster."""
"""Failed to retrieve server role."""


class ServerModeError(ArangoServerError):
"""Failed to retrieve server mode."""


class ServerModeSetError(ArangoServerError):
"""Failed to set server mode."""


class ServerTLSError(ArangoServerError):
Expand Down Expand Up @@ -980,7 +988,11 @@ class ClusterServerIDError(ArangoServerError):


class ClusterServerRoleError(ArangoServerError):
"""Failed to retrieve server role."""
"""Failed to retrieve server role in a cluster."""


class ClusterServerModeError(ArangoServerError):
"""Failed to retrieve server mode in a cluster."""


class ClusterServerStatisticsError(ArangoServerError):
Expand Down
15 changes: 14 additions & 1 deletion docs/admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,22 @@ database.
# Retrieve the server time.
sys_db.time()

# Retrieve the server role in a cluster.
# Retrieve the server role.
sys_db.role()

# Retrieve the server role in a cluster.
sys_db.cluster.server_role()

# Retrieve the server mode.
sys_db.mode()

# Retrieve the server mode in a cluster.
sys_db.cluster.server_mode()

# Set the server mode.
sys_db.set_mode('readonly')
sys_db.set_mode('default')

# Retrieve the server statistics.
sys_db.statistics()

Expand Down
13 changes: 13 additions & 0 deletions tests/test_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
ClusterServerCountError,
ClusterServerEngineError,
ClusterServerIDError,
ClusterServerModeError,
ClusterServerRoleError,
ClusterServerStatisticsError,
ClusterServerVersionError,
Expand Down Expand Up @@ -43,6 +44,18 @@ def test_cluster_server_role(sys_db, bad_db, cluster):
assert err.value.error_code in {FORBIDDEN, DATABASE_NOT_FOUND}


def test_cluster_server_mode(sys_db, bad_db, cluster):
if not cluster:
pytest.skip("Only tested in a cluster setup")

result = sys_db.cluster.server_mode()
assert result == "default"

with assert_raises(ClusterServerModeError) as err:
bad_db.cluster.server_mode()
assert err.value.error_code in {FORBIDDEN, DATABASE_NOT_FOUND}


def test_cluster_health(sys_db, bad_db, cluster):
if not cluster:
pytest.skip("Only tested in a cluster setup")
Expand Down
14 changes: 14 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
ServerLogLevelError,
ServerLogLevelSetError,
ServerMetricsError,
ServerModeSetError,
ServerReadLogError,
ServerReloadRoutingError,
ServerRequiredDBVersionError,
Expand Down Expand Up @@ -138,6 +139,19 @@ def test_database_misc_methods(client, sys_db, db, bad_db, cluster, secret):
bad_db.role()
assert err.value.error_code in {11, 1228}

# Test get/set server mode
assert sys_db.mode() == "default"
with assert_raises(ServerModeSetError):
sys_db.set_mode("badmode")
assert err.value.error_code in {11, 1228}

with assert_raises(ServerModeSetError):
db.set_mode("readonly")
assert err.value.error_code in {11, 1228}

result = sys_db.set_mode("default")
assert result == {"mode": "default"}

# Test get server status
status = db.status()
assert "host" in status
Expand Down

0 comments on commit 70548d9

Please sign in to comment.