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
1 change: 1 addition & 0 deletions doc/changelog.d/2419.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enhancements to transport mode
42 changes: 19 additions & 23 deletions src/ansys/geometry/core/connection/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@

import atexit
import logging
import os
from pathlib import Path
import time
from typing import Optional
import warnings

from beartype import beartype as check_input_types
import grpc
Expand All @@ -52,7 +50,7 @@

def _create_geometry_channel(
target: str,
transport_mode: str | None = None,
transport_mode: str,
uds_dir: Path | str | None = None,
uds_id: str | None = None,
certs_dir: Path | str | None = None,
Expand All @@ -64,9 +62,8 @@ def _create_geometry_channel(
target : str
Target of the channel. This is usually a string in the form of
``host:port``.
transport_mode : str | None
Transport mode selected, by default `None` and thus it will be selected
for you based on the connection criteria. Options are: "insecure", "uds", "wnua", "mtls"
transport_mode : str
Transport mode selected. Options are: "insecure", "uds", "wnua", "mtls"
uds_dir : Path | str | None
Directory to use for Unix Domain Sockets (UDS) transport mode.
By default `None` and thus it will use the "~/.conn" folder.
Expand Down Expand Up @@ -102,9 +99,9 @@ def _create_geometry_channel(

# Create the channel accordingly
return create_channel(
transport_mode=transport_mode,
host=host,
port=port,
transport_mode=transport_mode,
uds_service="aposdas_socket",
uds_dir=uds_dir,
uds_id=uds_id,
Expand Down Expand Up @@ -139,8 +136,8 @@ def wait_until_healthy(
* If the total elapsed time exceeds the value for the ``timeout`` parameter,
a ``TimeoutError`` is raised.
transport_mode : str | None
Transport mode selected, by default `None` and thus it will be selected
for you based on the connection criteria. Options are: "insecure", "uds", "wnua", "mtls"
Transport mode selected. Needed if channel is a string.
Options are: "insecure", "uds", "wnua", "mtls".
uds_dir : Path | str | None
Directory to use for Unix Domain Sockets (UDS) transport mode.
By default `None` and thus it will use the "~/.conn" folder.
Expand Down Expand Up @@ -168,23 +165,22 @@ def wait_until_healthy(
t_max = time.time() + timeout
t_out = 0.1

# If transport mode is not specified, default to insecure when running in CI
if transport_mode is None:
if os.getenv("IS_WORKFLOW_RUNNING") is not None:
warnings.warn(
"Transport mode forced to 'insecure' when running in CI workflows.",
)
transport_mode = "insecure"
else:
# If the channel is a string, create a channel using the specified transport mode
channel_creation_required = True if isinstance(channel, str) else False
tmp_channel = None

# If transport mode is not specified and a channel creation is required, raise an error
if channel_creation_required:
if transport_mode is None:
raise ValueError(
"Transport mode must be specified when not running in CI workflows."
"Transport mode must be specified."
" Use 'transport_mode' parameter with one of the possible options."
" Options are: 'insecure', 'uds', 'wnua', 'mtls'."
)
else:
from ansys.tools.common.cyberchannel import verify_transport_mode

# If the channel is a string, create a channel using the default insecure channel
channel_creation_required = True if isinstance(channel, str) else False
tmp_channel = None
verify_transport_mode(transport_mode)

while time.time() < t_max:
try:
Expand Down Expand Up @@ -262,8 +258,8 @@ class GrpcClient:
Protocol version to use for communication with the server. If None, v0 is used.
Available versions are "v0", "v1", etc.
transport_mode : str | None
Transport mode selected, by default `None` and thus it will be selected
for you based on the connection criteria. Options are: "insecure", "uds", "wnua", "mtls"
Transport mode selected. Needed if ``channel`` is not provided.
Options are: "insecure", "uds", "wnua", "mtls".
uds_dir : Path | str | None
Directory to use for Unix Domain Sockets (UDS) transport mode.
By default `None` and thus it will use the "~/.conn" folder.
Expand Down
25 changes: 25 additions & 0 deletions src/ansys/geometry/core/connection/docker_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ def __init__(
# Initialize instance variables
self._container: Container = None
self._existed_previously: bool = False
self._transport_mode: str | None = transport_mode

# Check the port availability
port_available, cont = self._check_port_availability(port)
Expand All @@ -197,6 +198,17 @@ def __init__(
# Finally, store the container
self._container = cont
self._existed_previously = True
# If no transport mode was provided, try to extract it from the existing
# container command line arguments
if transport_mode is None:
cmd_args = cont.attrs["Config"].get("Cmd", [])
for arg in cmd_args:
if "--transport-mode=" in arg:
self._transport_mode = arg.split("=")[1]
break
# If still None, default to "mtls"
if self._transport_mode is None:
self._transport_mode = "mtls"
return

# At this stage, confirm that you have to deploy our own Geometry service.
Expand Down Expand Up @@ -389,6 +401,7 @@ def _deploy_container(
# If the deployment went fine, this means that you have deployed the service.
self._container = container
self._existed_previously = False
self._transport_mode = transport_mode

@property
def container(self) -> "Container":
Expand All @@ -404,6 +417,18 @@ def existed_previously(self) -> bool:
"""
return self._existed_previously

@property
def transport_mode(self) -> str:
"""Transport mode used by the Docker container.

Returns
-------
str
Transport mode used by the Docker container. If no transport mode
was specified during initialization, it defaults to "mtls".
"""
return self._transport_mode if self._transport_mode else "mtls"


def get_geometry_container_type(instance: LocalDockerInstance) -> GeometryContainers | None:
"""Provide back the ``GeometryContainers`` value.
Expand Down
11 changes: 2 additions & 9 deletions src/ansys/geometry/core/connection/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import os
from pathlib import Path
from typing import TYPE_CHECKING
import warnings

from ansys.geometry.core.connection.backend import ApiVersions, BackendType
import ansys.geometry.core.connection.defaults as pygeom_defaults
Expand Down Expand Up @@ -340,7 +339,7 @@ def launch_docker_modeler(
in which case the client logs to the console.
transport_mode : str | None
Transport mode selected, by default `None` and thus it will be selected
for you based on the connection criteria. Options are: "insecure", "mtls"
for you based on the connection criteria. Options are: "insecure", "mtls".
certs_dir : Path | str | None
Directory to use for TLS certificates.
By default `None` and thus search for the "ANSYS_GRPC_CERTIFICATES" environment variable.
Expand All @@ -360,12 +359,6 @@ def launch_docker_modeler(
if not _HAS_DOCKER: # pragma: no cover
raise ModuleNotFoundError("The package 'docker' is required to use this function.")

if os.getenv("IS_WORKFLOW_RUNNING") is not None:
warnings.warn(
"Transport mode forced to 'insecure' when running in CI workflows.",
)
transport_mode = "insecure"

# Call the LocalDockerInstance ctor.
docker_instance = LocalDockerInstance(
port=port,
Expand All @@ -384,7 +377,7 @@ def launch_docker_modeler(
docker_instance=docker_instance,
logging_level=client_log_level,
logging_file=client_log_file,
transport_mode=transport_mode if transport_mode else "mtls",
transport_mode=docker_instance.transport_mode,
certs_dir=certs_dir,
)

Expand Down
15 changes: 14 additions & 1 deletion src/ansys/geometry/core/connection/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@

def validate(*args, **kwargs): # pragma: no cover
"""Create a client using the default settings and validate it."""
print(GrpcClient(*args, **kwargs))
# Assume local transport mode for validation if not provided
if "transport_mode" not in kwargs:
import platform

kwargs["transport_mode"] = "wnua" if platform.system() == "Windows" else "uds"
try:
GrpcClient(*args, **kwargs)
except Exception:
# Let's give it a try to insecure mode... just in case
kwargs["transport_mode"] = "insecure"
GrpcClient(*args, **kwargs)
else:
GrpcClient(*args, **kwargs)

# TODO: consider adding additional server stat reporting
# https://github.com/ansys/pyansys-geometry/issues/1319
4 changes: 2 additions & 2 deletions src/ansys/geometry/core/modeler.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ class Modeler:
Protocol version to use for communication with the server. If None, v0 is used.
Available versions are "v0", "v1", etc.
transport_mode : str | None
Transport mode selected, by default `None` and thus it will be selected
for you based on the connection criteria. Options are: "insecure", "uds", "wnua", "mtls"
Transport mode selected. Needed if ``channel`` is not provided.
Options are: "insecure", "uds", "wnua", "mtls".
uds_dir : Path | str | None
Directory to use for Unix Domain Sockets (UDS) transport mode.
By default `None` and thus it will use the "~/.conn" folder.
Expand Down
11 changes: 5 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ def pytest_addoption(parser):
parser.addoption(
"--transport-mode",
action="store",
default="default",
help=("Specify the transport mode to use for the tests. By default, 'default'."),
choices=("default", "insecure", "uds", "wnua", "mtls"),
default="insecure",
help=("Specify the transport mode to use for the tests. By default, 'insecure'."),
choices=("insecure", "uds", "wnua", "mtls"),
)


Expand Down Expand Up @@ -184,10 +184,9 @@ def proto_version(request):
def transport_mode(request):
"""Fixture to determine transport mode to be used."""

value: str = request.config.getoption("--transport-mode", default="default")
mode = None if value.lower() == "default" else value.lower()
value: str = request.config.getoption("--transport-mode", default="insecure")

return mode
return value.lower()


@pytest.fixture
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@


@pytest.fixture(scope="function")
def client(modeler: Modeler):
def client(modeler: Modeler, transport_mode: str) -> GrpcClient:
# this uses DEFAULT_HOST and DEFAULT_PORT which are set by environment
# variables in the workflow
return GrpcClient()
return GrpcClient(transport_mode=transport_mode)


def test_client_init(client: GrpcClient):
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/test_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ def test_upload_file(modeler: Modeler, tmp_path_factory: pytest.TempPathFactory)
assert path_on_server is not None


def test_stream_upload_file(tmp_path_factory: pytest.TempPathFactory):
def test_stream_upload_file(tmp_path_factory: pytest.TempPathFactory, transport_mode: str):
"""Test uploading a file to the server."""
# Define a new maximum message length
import ansys.geometry.core.connection.defaults as pygeom_defaults
Expand All @@ -1333,7 +1333,7 @@ def test_stream_upload_file(tmp_path_factory: pytest.TempPathFactory):
# Upload file - necessary to import the Modeler class and create an instance
from ansys.geometry.core import Modeler

modeler = Modeler()
modeler = Modeler(transport_mode=transport_mode)
path_on_server = modeler._upload_file_stream(file)
assert path_on_server is not None
finally:
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/test_design_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def _checker_method(comp: Component, comp_ref: Component, precise_check: bool =
_checker_method(subcomp, subcomp_ref, precise_check)


def test_design_import_simple_case(modeler: Modeler):
def test_design_import_simple_case(modeler: Modeler, transport_mode: str):
# With the given session let's create a the following Design
#
# Create your design on the server side
Expand Down Expand Up @@ -157,7 +157,7 @@ def test_design_import_simple_case(modeler: Modeler):
)

# Now, let's create a new client session
new_client = Modeler()
new_client = Modeler(transport_mode=transport_mode)
read_design = new_client.read_existing_design()

# And now assert all its elements
Expand Down
4 changes: 2 additions & 2 deletions tests/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@
LOG_LEVELS = {"CRITICAL": 50, "ERROR": 40, "WARNING": 30, "INFO": 20, "DEBUG": 10}


def test_add_instance():
def test_add_instance(transport_mode: str):
"""Testing adding an instance logger while checking if log has certain key"""
base_name = "root"
instance_logger_1 = LOG.add_instance_logger(
name=base_name, client_instance=GrpcClient(), level=10
name=base_name, client_instance=GrpcClient(transport_mode=transport_mode), level=10
)
instance_logger_1.info("This is a message from the first instance logger.")
with pytest.raises(KeyError, match="There is no instances with name root_4."):
Expand Down
Loading