cs3client 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cs3client/__init__.py ADDED
File without changes
cs3client/app.py ADDED
@@ -0,0 +1,84 @@
1
+ """
2
+ app.py
3
+
4
+ Authors: Rasmus Welander, Diogo Castro, Giuseppe Lo Presti.
5
+ Emails: rasmus.oscar.welander@cern.ch, diogo.castro@cern.ch, giuseppe.lopresti@cern.ch
6
+ Last updated: 30/08/2024
7
+ """
8
+
9
+ import logging
10
+ import cs3.app.registry.v1beta1.registry_api_pb2 as cs3arreg
11
+ import cs3.app.registry.v1beta1.resources_pb2 as cs3arres
12
+ import cs3.gateway.v1beta1.gateway_api_pb2 as cs3gw
13
+ import cs3.app.provider.v1beta1.resources_pb2 as cs3apr
14
+ from cs3.gateway.v1beta1.gateway_api_pb2_grpc import GatewayAPIStub
15
+
16
+ from .cs3resource import Resource
17
+ from .statuscodehandler import StatusCodeHandler
18
+ from .config import Config
19
+
20
+
21
+ class App:
22
+ """
23
+ App class to handle app related API calls with CS3 Gateway API.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ config: Config,
29
+ log: logging.Logger,
30
+ gateway: GatewayAPIStub,
31
+ status_code_handler: StatusCodeHandler,
32
+ ) -> None:
33
+ """
34
+ Initializes the App class with configuration, logger, auth, and gateway stub,
35
+
36
+ :param config: Config object containing the configuration parameters.
37
+ :param log: Logger instance for logging.
38
+ :param gateway: GatewayAPIStub instance for interacting with CS3 Gateway.
39
+ :param status_code_handler: An instance of the StatusCodeHandler class.
40
+ """
41
+ self._status_code_handler: StatusCodeHandler = status_code_handler
42
+ self._gateway: GatewayAPIStub = gateway
43
+ self._log: logging.Logger = log
44
+ self._config: Config = config
45
+
46
+ def open_in_app(
47
+ self, auth_token: tuple, resource: Resource, view_mode: str = None, app: str = None
48
+ ) -> cs3apr.OpenInAppURL:
49
+ """
50
+ Open a file in an app, given the resource, view mode (VIEW_MODE_VIEW_ONLY, VIEW_MODE_READ_ONLY,
51
+ VIEW_MODE_READ_WRITE, VIEW_MODE_PREVIEW), and app name.
52
+
53
+ :param auth_token: tuple in the form ('x-access-token', <token>) (see auth.get_token/auth.check_token)
54
+ :param resource: Resource object containing the resource information.
55
+ :param view_mode: View mode of the app.
56
+ :param app: App name.
57
+ :return: URL to open the file in the app.
58
+ :raises: AuthenticationException (Operation not permitted)
59
+ :raises: NotFoundException (Resource not found)
60
+ :raises: UnknownException (Unknown error)
61
+ """
62
+ view_mode_type = None
63
+ if view_mode:
64
+ view_mode_type = cs3gw.OpenInAppRequest.ViewMode.Value(view_mode)
65
+ req = cs3gw.OpenInAppRequest(ref=resource.ref, view_mode=view_mode_type, app=app)
66
+ res = self._gateway.OpenInApp(request=req, metadata=[auth_token])
67
+ self._status_code_handler.handle_errors(res.status, "open in app", f"{resource.get_file_ref_str()}")
68
+ self._log.debug(f'msg="Invoked OpenInApp" {resource.get_file_ref_str()} trace="{res.status.trace}"')
69
+ return res.OpenInAppURL
70
+
71
+ def list_app_providers(self, auth_token: dict) -> list[cs3arres.ProviderInfo]:
72
+ """
73
+ list_app_providers lists all the app providers.
74
+
75
+ :param auth_token: tuple in the form ('x-access-token', <token>) (see auth.get_token/auth.check_token)
76
+ :return: List of app providers.
77
+ :raises: AuthenticationException (Operation not permitted)
78
+ :raises: UnknownException (Unknown error)
79
+ """
80
+ req = cs3arreg.ListAppProvidersRequest()
81
+ res = self._gateway.ListAppProviders(request=req, metadata=[auth_token])
82
+ self._status_code_handler.handle_errors(res.status, "list app providers")
83
+ self._log.debug(f'msg="Invoked ListAppProviders" res_count="{len(res.providers)}" trace="{res.status.trace}"')
84
+ return res.providers
cs3client/auth.py ADDED
@@ -0,0 +1,141 @@
1
+ """
2
+ auth.py
3
+
4
+ Authors: Rasmus Welander, Diogo Castro, Giuseppe Lo Presti.
5
+ Emails: rasmus.oscar.welander@cern.ch, diogo.castro@cern.ch, giuseppe.lopresti@cern.ch
6
+ Last updated: 30/08/2024
7
+ """
8
+
9
+ import grpc
10
+ import jwt
11
+ import datetime
12
+ import logging
13
+ from typing import Union
14
+ from cs3.gateway.v1beta1.gateway_api_pb2 import AuthenticateRequest
15
+ from cs3.auth.registry.v1beta1.registry_api_pb2 import ListAuthProvidersRequest
16
+ from cs3.gateway.v1beta1.gateway_api_pb2_grpc import GatewayAPIStub
17
+ from cs3.rpc.v1beta1.code_pb2 import CODE_OK
18
+
19
+ from .cs3client import CS3Client
20
+ from .exceptions.exceptions import AuthenticationException, SecretNotSetException
21
+ from .config import Config
22
+
23
+
24
+ class Auth:
25
+ """
26
+ Auth class to handle authentication and token validation with CS3 Gateway API.
27
+ """
28
+
29
+ def __init__(self, cs3_client: CS3Client) -> None:
30
+ """
31
+ Initializes the Auth class with configuration, logger, and gateway stub,
32
+ NOTE that token OR the client secret has to be set when instantiating the auth object.
33
+
34
+ :param config: Config object containing the configuration parameters.
35
+ :param log: Logger instance for logging.
36
+ :param gateway: GatewayAPIStub instance for interacting with CS3 Gateway.
37
+ """
38
+ self._gateway: GatewayAPIStub = cs3_client._gateway
39
+ self._log: logging.Logger = cs3_client._log
40
+ self._config: Config = cs3_client._config
41
+ # The user should be able to change the client secret (e.g. token) and client id at runtime
42
+ self._client_secret: Union[str, None] = self._config.auth_client_secret
43
+ self._client_id: Union[str, None] = self._config.auth_client_id
44
+ self._token: Union[str, None] = None
45
+
46
+ def set_client_secret(self, token: str) -> None:
47
+ """
48
+ Sets the client secret, exists so that the user can change the client secret (e.g. token, password) at runtime,
49
+ without having to create a new Auth object. Note client secret has to be set when
50
+ instantiating the client object or through the configuration.
51
+
52
+ :param token: Auth token/password.
53
+ """
54
+ self._client_secret = token
55
+
56
+ def set_client_id(self, id: str) -> None:
57
+ """
58
+ Sets the client id, exists so that the user can change the client id at runtime, without having to create
59
+ a new Auth object. Settings this (either through config or here) is optional unless you are using
60
+ basic authentication.
61
+
62
+ :param token: id.
63
+ """
64
+ self._client_id = id
65
+
66
+ def get_token(self) -> tuple[str, str]:
67
+ """
68
+ Attempts to get a valid authentication token. If the token is not valid, a new token is requested
69
+ if the client secret is set, if only the token is set then an exception will be thrown stating that
70
+ the credentials have expired.
71
+
72
+ :return tuple: A tuple containing the header key and the token.
73
+ :raises: AuthenticationException (token expired, or failed to authenticate)
74
+ :raises: SecretNotSetException (neither token or client secret was set)
75
+ """
76
+
77
+ if not self._client_secret:
78
+ self._log.error("Attempted to authenticate, client secret was not set")
79
+ raise SecretNotSetException("The client secret (e.g. token, passowrd) is not set")
80
+
81
+ try:
82
+ self.check_token(self._token)
83
+ except AuthenticationException:
84
+ # Token has expired or has not been set, obtain another one.
85
+ req = AuthenticateRequest(
86
+ type=self._config.auth_login_type,
87
+ client_id=self._client_id,
88
+ client_secret=self._client_secret,
89
+ )
90
+ # Send the authentication request to the CS3 Gateway
91
+ res = self._gateway.Authenticate(req)
92
+
93
+ if res.status.code != CODE_OK:
94
+ self._log.error(f'msg="Failed to authenticate" '
95
+ f'user="{self._client_id if self._client_id else "no_id_set"}" '
96
+ f'error_code="{res.status}"')
97
+ raise AuthenticationException(
98
+ f'Failed to authenticate: user="{self._client_id if self._client_id else "no_id_set"}" '
99
+ f'error_code="{res.status}"'
100
+ )
101
+ self._token = res.token
102
+ self._log.debug(f'msg="Authenticated user" user="{self._client_id if self._client_id else "no_id_set"}"')
103
+ return ("x-access-token", self._token)
104
+
105
+ def list_auth_providers(self) -> list[str]:
106
+ """
107
+ list authentication providers
108
+
109
+ :return: a list of the supported authentication types
110
+ :raises: ConnectionError (Could not connect to host)
111
+ """
112
+ try:
113
+ res = self._gateway.ListAuthProviders(request=ListAuthProvidersRequest())
114
+ if res.status.code != CODE_OK:
115
+ self._log.error(f'msg="List auth providers request failed" error_code="{res.status}"')
116
+ raise Exception(res.status.message)
117
+ except grpc.RpcError as e:
118
+ self._log.error("List auth providers request failed")
119
+ raise ConnectionError(e)
120
+ return res.types
121
+
122
+ @classmethod
123
+ def check_token(cls, token: str) -> tuple:
124
+ """
125
+ Checks if the given token is set and valid.
126
+
127
+ :param token: JWT token as a string.
128
+ :return tuple: A tuple containing the header key and the token.
129
+ :raises: ValueError (Token missing)
130
+ :raises: AuthenticationException (Token is expired)
131
+ """
132
+ if not token:
133
+ raise AuthenticationException("Token not set")
134
+ # Decode the token without verifying the signature
135
+ decoded_token = jwt.decode(jwt=token, algorithms=["HS256"], options={"verify_signature": False})
136
+ now = datetime.datetime.now().timestamp()
137
+ token_expiration = decoded_token.get("exp")
138
+ if token_expiration and now > token_expiration:
139
+ raise AuthenticationException("Token has expired")
140
+
141
+ return ("x-access-token", token)
@@ -0,0 +1,86 @@
1
+ """
2
+ checkpoint.py
3
+
4
+ Authors: Rasmus Welander, Diogo Castro, Giuseppe Lo Presti.
5
+ Emails: rasmus.oscar.welander@cern.ch, diogo.castro@cern.ch, giuseppe.lopresti@cern.ch
6
+ Last updated: 30/08/2024
7
+ """
8
+
9
+ from typing import Generator
10
+ import logging
11
+ import cs3.storage.provider.v1beta1.resources_pb2 as cs3spr
12
+ import cs3.storage.provider.v1beta1.provider_api_pb2 as cs3spp
13
+ from cs3.gateway.v1beta1.gateway_api_pb2_grpc import GatewayAPIStub
14
+
15
+ from .config import Config
16
+ from .statuscodehandler import StatusCodeHandler
17
+ from .cs3resource import Resource
18
+
19
+
20
+ class Checkpoint:
21
+ """
22
+ Checkpoint class to handle checkpoint related API calls with CS3 Gateway API.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ config: Config,
28
+ log: logging.Logger,
29
+ gateway: GatewayAPIStub,
30
+ status_code_handler: StatusCodeHandler,
31
+ ) -> None:
32
+ """
33
+ Initializes the checkpoint class with configuration, logger, auth, and gateway stub,
34
+
35
+ :param config: Config object containing the configuration parameters.
36
+ :param log: Logger instance for logging.
37
+ :param gateway: GatewayAPIStub instance for interacting with CS3 Gateway.
38
+ :param status_code_handler: An instance of the StatusCodeHandler class.
39
+ """
40
+ self._gateway: GatewayAPIStub = gateway
41
+ self._log: logging.Logger = log
42
+ self._config: Config = config
43
+ self._status_code_handler: StatusCodeHandler = status_code_handler
44
+
45
+ def list_file_versions(
46
+ self, auth_token: tuple, resource: Resource, page_token: str = "", page_size: int = 0
47
+ ) -> Generator[cs3spr.FileVersion, any, any]:
48
+ """
49
+ List all versions of a file.
50
+
51
+ :param auth_token: tuple in the form ('x-access-token', <token>) (see auth.get_token/auth.check_token)
52
+ :param resource: Resource object containing the resource information.
53
+ :param page_token: Token for pagination.
54
+ :param page_size: Number of file versions to return.
55
+ (default is zero, where the server decides the number of versions to return)
56
+ :return: List of file versions (None if no versions are found).
57
+ :raises: AuthenticationException (Operation not permitted)
58
+ :raises: NotFoundException (File not found)
59
+ :raises: UnknownException (Unknown error)
60
+ """
61
+ req = cs3spp.ListFileVersionsRequest(ref=resource.ref, page_token=page_token, page_size=page_size)
62
+ res = self._gateway.ListFileVersions(request=req, metadata=[auth_token])
63
+ self._status_code_handler.handle_errors(res.status, "list file versions", f"{resource.get_file_ref_str()}")
64
+ self._log.debug(f'msg="list file versions" {resource.get_file_ref_str()} trace="{res.status.trace}"')
65
+ return res.versions
66
+
67
+ def restore_file_version(
68
+ self, auth_token: tuple, resource: Resource, version_key: str, lock_id: str = None
69
+ ) -> None:
70
+ """
71
+ Restore a file to a previous version.
72
+
73
+ :param auth_token: tuple in the form ('x-access-token', <token>) (see auth.get_token/auth.check_token)
74
+ :param resource: Resource object containing the resource information.
75
+ :param version_key: Key of the version to restore.
76
+ :param lock_id: Lock ID of the file (OPTIONAL).
77
+ :return: None (Success)
78
+ :raises: AuthenticationException (Operation not permitted)
79
+ :raises: NotFoundException (File not found)
80
+ :raises: UnknownException (Unknown error)
81
+ """
82
+ req = cs3spp.RestoreFileVersionRequest(ref=resource.ref, key=version_key, lock_id=lock_id)
83
+ res = self._gateway.RestoreFileVersion(request=req, metadata=[auth_token])
84
+ self._status_code_handler.handle_errors(res.status, "restore file version", f"{resource.get_file_ref_str()}")
85
+ self._log.debug(f'msg="restore file version" {resource.get_file_ref_str()} trace="{res.status.trace}"')
86
+ return
cs3client/config.py ADDED
@@ -0,0 +1,198 @@
1
+ """
2
+ config.py
3
+
4
+ Authors: Rasmus Welander, Diogo Castro, Giuseppe Lo Presti.
5
+ Emails: rasmus.oscar.welander@cern.ch, diogo.castro@cern.ch, giuseppe.lopresti@cern.ch
6
+ Last updated: 29/07/2024
7
+ """
8
+
9
+ from configparser import ConfigParser
10
+
11
+
12
+ class Config:
13
+ """
14
+ A class to read and store the configuration parameters provided to the CS3 client.
15
+ """
16
+
17
+ def __init__(self, config: ConfigParser, config_category: str) -> None:
18
+ """
19
+ Initializes the Config class with the configuration parameters.
20
+
21
+ :param config: Dictionary containing configuration parameters.
22
+ :param config_category: The category of the configuration parameters.
23
+ """
24
+ self._config_category: str = config_category
25
+ self._config: ConfigParser = config
26
+
27
+ @property
28
+ def host(self) -> str:
29
+ """
30
+ The host property returns the host address and port from the configuration.
31
+
32
+ :return: host address:port
33
+ """
34
+ return self._config.get(self._config_category, "host")
35
+
36
+ @property
37
+ def chunk_size(self) -> int:
38
+ """
39
+ The chunk_size property returns the chunk_size value from the configuration,
40
+ fallback to 4194304 if not present.
41
+
42
+ :return: The chunk size value.
43
+ """
44
+ return self._config.getint(self._config_category, "chunk_size", fallback=4194304)
45
+
46
+ @property
47
+ def grpc_timeout(self) -> int:
48
+ """
49
+ The grpc_timeout property returns the grpc_timeout value from the configuration,
50
+ fallback to 10 if not present.
51
+
52
+ :return: The grpc timeout value.
53
+ """
54
+ return self._config.getint(self._config_category, "grpc_timeout", fallback=10)
55
+
56
+ @property
57
+ def http_timeout(self) -> int:
58
+ """
59
+ The http_timeout property returns the http_timeout value from the configuration,
60
+ fallback to 10 if not present.
61
+
62
+ :return: The http timeout value.
63
+ """
64
+ return self._config.getint(self._config_category, "http_timeout", fallback=10)
65
+
66
+ @property
67
+ def ssl_enabled(self) -> bool:
68
+ """
69
+ The ssl_enabled property returns the ssl_enabled value from the configuration,
70
+ fallback to True if not present.
71
+
72
+ :return: ssl_enabled value.
73
+ """
74
+ return self._config.getboolean(self._config_category, "ssl_enabled", fallback=False)
75
+
76
+ @property
77
+ def ssl_verify(self) -> bool:
78
+ """
79
+ The ssl_verify property returns the ssl_verify value from the configuration,
80
+
81
+ :return: ssl_verify
82
+ """
83
+ return self._config.getboolean(self._config_category, "ssl_verify", fallback=False)
84
+
85
+ @property
86
+ def ssl_client_cert(self) -> str:
87
+ """
88
+ The ssl_client_cert property returns the ssl_client_cert value from the configuration,
89
+ if not present, fallback to an empty string since it is not required if ssl is not enabled.
90
+
91
+ :return: ssl_client_cert
92
+ """
93
+ return self._config.get(self._config_category, "ssl_client_cert", fallback=None)
94
+
95
+ @property
96
+ def ssl_client_key(self) -> str:
97
+ """
98
+ The ssl_client_key property returns the ssl_client_key value from the configuration,
99
+ if not present, fallback to an empty string since it is not required if ssl is not enabled.
100
+
101
+ :return: ssl_client_key
102
+ """
103
+ return self._config.get(self._config_category, "ssl_client_key", fallback=None)
104
+
105
+ @property
106
+ def ssl_ca_cert(self) -> str:
107
+ """
108
+ The ssl_ca_cert property returns the ssl_ca_cert value from the configuration,
109
+ if not present, fallback to an empty string since it is not required if ssl is not enabled.
110
+
111
+ :return: ssl_ca_cert
112
+ """
113
+ return self._config.get(self._config_category, "ssl_ca_cert", fallback=None)
114
+
115
+ @property
116
+ def auth_login_type(self) -> str:
117
+ """
118
+ The auth_login_type property returns the auth_login_type value from the configuration.
119
+ e.g. basic, bearer, oauth, machine.
120
+
121
+ :return: auth_login_type
122
+ """
123
+ return self._config.get(self._config_category, "auth_login_type", fallback="basic")
124
+
125
+ @property
126
+ def auth_client_id(self) -> str:
127
+ """
128
+ The auth_client_id property returns the auth_client_id value from the configuration,
129
+
130
+ :return: auth_client_id
131
+ """
132
+ return self._config.get(self._config_category, "auth_client_id", fallback=None)
133
+
134
+ @property
135
+ def auth_client_secret(self) -> str:
136
+ """
137
+ The auth_client_secret property returns the auth_client_secret value from the configuration,
138
+
139
+ :return: auth_client_secret
140
+ """
141
+ return self._config.get(self._config_category, "auth_client_secret", fallback=None)
142
+
143
+ @property
144
+ def tus_enabled(self) -> bool:
145
+ """
146
+ The tus_enabled property returns the tus_enabled value from the configuration,
147
+
148
+ :return: tus_enabled
149
+ """
150
+ return self._config.getboolean(self._config_category, "tus_enabled", fallback=False)
151
+
152
+ # For the lock implementation
153
+ @property
154
+ def lock_by_setting_attr(self) -> bool:
155
+ """
156
+ The lock_by_setting_attr property returns the lock_by_setting_attr value from the configuration,
157
+ fallback to False if not present.
158
+
159
+ The lock by setting attribute setting should be set if the storage provider does not
160
+ implement locking functionality. In which case the client will use the fallback mechanism
161
+ by locking resources by setting metadata attributes. If lock_not_impl is set to false and
162
+ lock_by_setting_attr is set to true, the client will attempt to lock normally first,
163
+ and if that fails, it will attempt to lock by setting metadata attributes.
164
+
165
+
166
+ :return: lock_by_setting_attr
167
+ """
168
+ return self._config.getboolean(self._config_category, "lock_by_setting_attr", fallback=False)
169
+
170
+ # For the lock implementation
171
+ @property
172
+ def lock_not_impl(self) -> bool:
173
+ """
174
+ The lock_not_impl property returns the lock_not_impl value from the configuration,
175
+ fallback to False if not present.
176
+
177
+ The lock not implemented setting should be set if the storage provider
178
+ does not implement locking functionality. In which case the client will use the
179
+ fallback mechanism by locking resources by setting metadata attributes if the
180
+ lock_by_setting_attr is set to True.
181
+
182
+ :return: lock_not_impl
183
+ """
184
+ return self._config.getboolean(self._config_category, "lock_not_impl", fallback=False)
185
+
186
+ # For the lock implementation
187
+ @property
188
+ def lock_expiration(self) -> int:
189
+ """
190
+ The lock_expiration property returns the lock_expiration value from the configuration,
191
+ fallback to 1800 if not present.
192
+
193
+ The lock expiration setting is used to determine the time
194
+ in seconds before a lock is considered expired.
195
+
196
+ :return: lock_expiration
197
+ """
198
+ return self._config.getint(self._config_category, "lock_expiration", fallback=1800)
cs3client/cs3client.py ADDED
@@ -0,0 +1,73 @@
1
+ """
2
+ cs3client.py
3
+
4
+ Authors: Rasmus Welander, Diogo Castro, Giuseppe Lo Presti.
5
+ Emails: rasmus.oscar.welander@cern.ch, diogo.castro@cern.ch, giuseppe.lopresti@cern.ch
6
+ Last updated: 30/08/2024
7
+ """
8
+
9
+ import grpc
10
+ import logging
11
+ import cs3.gateway.v1beta1.gateway_api_pb2_grpc as cs3gw_grpc
12
+ from configparser import ConfigParser
13
+
14
+ from .file import File
15
+ from .user import User
16
+ from .share import Share
17
+ from .statuscodehandler import StatusCodeHandler
18
+ from .app import App
19
+ from .checkpoint import Checkpoint
20
+ from .config import Config
21
+
22
+
23
+ class CS3Client:
24
+ """
25
+ Client class to interact with the CS3 API.
26
+ """
27
+
28
+ def __init__(self, config: ConfigParser, config_category: str, log: logging.Logger) -> None:
29
+ """
30
+ Initializes the CS3Client class with configuration and logger.
31
+ :param config: Dictionary containing configuration parameters.
32
+ :param config_category: Category of the configuration parameters.
33
+ :param log: Logger instance for logging.
34
+ """
35
+
36
+ self._config: Config = Config(config, config_category)
37
+ self._log: logging.Logger = log
38
+
39
+ try:
40
+ self.channel: grpc.Channel = self._create_channel()
41
+ grpc.channel_ready_future(self.channel).result(timeout=self._config.grpc_timeout)
42
+ except grpc.FutureTimeoutError as e:
43
+ log.error(f'msg="Failed to connect to Reva via GRPC" error="{e}"')
44
+ raise TimeoutError("Failed to connect to Reva via GRPC")
45
+
46
+ self._gateway: cs3gw_grpc.GatewayAPIStub = cs3gw_grpc.GatewayAPIStub(self.channel)
47
+ self._status_code_handler: StatusCodeHandler = StatusCodeHandler(self._log, self._config)
48
+ self.file: File = File(self._config, self._log, self._gateway, self._status_code_handler)
49
+ self.user: User = User(self._config, self._log, self._gateway, self._status_code_handler)
50
+ self.app: App = App(self._config, self._log, self._gateway, self._status_code_handler)
51
+ self.checkpoint: Checkpoint = Checkpoint(
52
+ self._config, self._log, self._gateway, self._status_code_handler
53
+ )
54
+ self.share = Share(self._config, self._log, self._gateway, self._status_code_handler)
55
+
56
+ def _create_channel(self) -> grpc.Channel:
57
+ """
58
+ create_channel creates a gRPC channel to the specified host.
59
+
60
+ :return: gRPC channel to the specified host.
61
+ """
62
+
63
+ if self._config.ssl_enabled:
64
+
65
+ credentials = grpc.ssl_channel_credentials(
66
+ root_certificates=self._config.ssl_ca_cert,
67
+ private_key=self._config.ssl_client_key,
68
+ certificate_chain=self._config.ssl_client_cert,
69
+ )
70
+ channel = grpc.secure_channel(self._config.host, credentials)
71
+ else:
72
+ channel = grpc.insecure_channel(self._config.host)
73
+ return channel