oscar-python 1.3.4__tar.gz → 2.0.0__tar.gz

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.
Files changed (39) hide show
  1. {oscar_python-1.3.4 → oscar_python-2.0.0}/PKG-INFO +1 -1
  2. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/_providers/_s3.py +1 -1
  3. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/_utils.py +22 -4
  4. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/client.py +120 -6
  5. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/local_test.py +1 -1
  6. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/storage.py +3 -4
  7. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python.egg-info/PKG-INFO +1 -1
  8. oscar_python-2.0.0/tests/test_client.py +324 -0
  9. {oscar_python-1.3.4 → oscar_python-2.0.0}/tests/test_default_client.py +6 -2
  10. {oscar_python-1.3.4 → oscar_python-2.0.0}/tests/test_storage.py +5 -1
  11. {oscar_python-1.3.4 → oscar_python-2.0.0}/tests/test_utils.py +68 -1
  12. {oscar_python-1.3.4 → oscar_python-2.0.0}/tests/test_webdav.py +1 -1
  13. oscar_python-1.3.4/tests/test_client.py +0 -138
  14. {oscar_python-1.3.4 → oscar_python-2.0.0}/.github/workflows/release-build.yaml +0 -0
  15. {oscar_python-1.3.4 → oscar_python-2.0.0}/.github/workflows/tests.yaml +0 -0
  16. {oscar_python-1.3.4 → oscar_python-2.0.0}/.gitignore +0 -0
  17. {oscar_python-1.3.4 → oscar_python-2.0.0}/LICENSE +0 -0
  18. {oscar_python-1.3.4 → oscar_python-2.0.0}/README.md +0 -0
  19. {oscar_python-1.3.4 → oscar_python-2.0.0}/jupyter_example/oscar_notebook.ipynb +0 -0
  20. {oscar_python-1.3.4 → oscar_python-2.0.0}/jupyter_example/services/cowsay_example/cowsay.yaml +0 -0
  21. {oscar_python-1.3.4 → oscar_python-2.0.0}/jupyter_example/services/cowsay_example/script.sh +0 -0
  22. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/__init__.py +0 -0
  23. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/_oidc.py +0 -0
  24. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/_providers/_minio.py +0 -0
  25. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/_providers/_onedata.py +0 -0
  26. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/_providers/_providers_base.py +0 -0
  27. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/_providers/_webdav.py +0 -0
  28. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/client_anon.py +0 -0
  29. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python/default_client.py +0 -0
  30. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python.egg-info/SOURCES.txt +0 -0
  31. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python.egg-info/dependency_links.txt +0 -0
  32. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python.egg-info/requires.txt +0 -0
  33. {oscar_python-1.3.4 → oscar_python-2.0.0}/oscar_python.egg-info/top_level.txt +0 -0
  34. {oscar_python-1.3.4 → oscar_python-2.0.0}/pyproject.toml +0 -0
  35. {oscar_python-1.3.4 → oscar_python-2.0.0}/setup.cfg +0 -0
  36. {oscar_python-1.3.4 → oscar_python-2.0.0}/setup.py +0 -0
  37. {oscar_python-1.3.4 → oscar_python-2.0.0}/tests/test_oidc.py +0 -0
  38. {oscar_python-1.3.4 → oscar_python-2.0.0}/tests/test_onedata.py +0 -0
  39. {oscar_python-1.3.4 → oscar_python-2.0.0}/tests/test_s3.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oscar-python
3
- Version: 1.3.4
3
+ Version: 2.0.0
4
4
  Summary: Python client for OSCAR clusters
5
5
  Author: GRyCAP - I3M - UPV
6
6
  License: Apache-2.0
@@ -59,7 +59,7 @@ class S3(StorageProvider):
59
59
  bucket_name = remote_path.split('/')[0]
60
60
  file_key = remote_path.split('/', 1)[1]
61
61
  file_name = local_path.split('/')[-1]
62
- print("Uploading to bucket '{0}' with key '{1}'".format(bucket_name,file_key))
62
+ print("Uploading to bucket '{0}' with key '{1}'".format(bucket_name, file_key))
63
63
  with open(local_path, 'rb') as data:
64
64
  try:
65
65
  self.client.upload_fileobj(data, bucket_name, file_key + "/" + file_name)
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import base64
16
+ import json
16
17
  import os
17
18
  import requests
18
19
  import liboidcagent as agent
@@ -32,14 +33,16 @@ def make_request(c, path, method, **kwargs):
32
33
  url = c.endpoint+path
33
34
 
34
35
  if method in ["post", "put"]:
35
- if "token" in kwargs.keys() and kwargs["token"]:
36
+ if "token" in kwargs.keys() and kwargs["token"]:
36
37
  headers = get_headers_with_token(kwargs["token"])
38
+ req_kwargs = {"headers": headers, "verify": c.ssl, "timeout": timeout}
37
39
  if "data" in kwargs.keys() and kwargs["data"]:
38
- result = requests.request(method, url, headers=headers, verify=c.ssl, data=kwargs["data"], timeout=timeout)
40
+ req_kwargs["data"] = kwargs["data"]
41
+ result = requests.request(method, url, **req_kwargs)
39
42
  else:
40
43
  result = requests.request(method, url, headers=headers, verify=c.ssl, timeout=timeout)
41
44
 
42
- if "handle" in kwargs.keys() and kwargs["handle"] == False:
45
+ if "handle" in kwargs.keys() and kwargs["handle"] is False:
43
46
  return result
44
47
 
45
48
  result.raise_for_status()
@@ -94,7 +97,7 @@ def decode_b64(b64_str, file_out):
94
97
  except ValueError:
95
98
  print('Error decoding output: Invalid base64 string.')
96
99
  except OSError:
97
- print('Error decoding output: Failed to write decoded data to file.')
100
+ print('Error decoding output: Failed to write decoded data to file.')
98
101
 
99
102
 
100
103
  def encode_input(data):
@@ -111,6 +114,21 @@ def encode_input(data):
111
114
  return base64.b64encode(message_bytes)
112
115
 
113
116
 
117
+ def load_config(config_path, cluster_name="default"):
118
+ with open(config_path) as f:
119
+ config = json.load(f)
120
+ cluster = config["clusters"][cluster_name]
121
+ opts = {
122
+ "cluster_id": cluster_name,
123
+ "endpoint": cluster["endpoint"],
124
+ "ssl": cluster.get("ssl", True),
125
+ }
126
+ for key in ("user", "password", "shortname", "oidc_token", "refresh_token"):
127
+ if key in cluster:
128
+ opts[key] = cluster[key]
129
+ return opts
130
+
131
+
114
132
  def decode_output(output, file_path):
115
133
  if isBase64(output):
116
134
  decode_b64(output, file_path)
@@ -27,7 +27,12 @@ _CONFIG_PATH = "/system/config"
27
27
  _SVC_PATH = "/system/services"
28
28
  _LOGS_PATH = "/system/logs"
29
29
  _RUN_PATH = "/run"
30
- _STATUS_PATH="/system/status"
30
+ _STATUS_PATH = "/system/status"
31
+ _HEALTH_PATH = "/health"
32
+ _VOLUMES_PATH = "/system/volumes"
33
+ _BUCKETS_PATH = "/system/buckets"
34
+ _METRICS_PATH = "/system/metrics"
35
+ _QUOTAS_USER_PATH = "/system/quotas/user"
31
36
 
32
37
 
33
38
  # _JOB_PATH = "/job"
@@ -101,13 +106,13 @@ class Client(DefaultClient):
101
106
  """ Creates a generic storage client to interact with the storage providers
102
107
  defined on a specific service of the refered OSCAR cluster """
103
108
  def create_storage_client(self, svc=None):
104
- if svc != None:
109
+ if svc is not None:
105
110
  return Storage(
106
111
  client_obj=self, svc_name=svc)
107
112
  else:
108
113
  return Storage(
109
114
  client_obj=self)
110
-
115
+
111
116
  """ Function to get cluster info """
112
117
  def get_cluster_info(self):
113
118
  return utils.make_request(self, _INFO_PATH, _GET)
@@ -137,17 +142,18 @@ class Client(DefaultClient):
137
142
  raise Exception("FDL clusterID does not match current clusterID: {0}".format(err))
138
143
  try:
139
144
  if os.path.isabs(svc["script"]):
140
- script_path = svc["script"]
145
+ script_path = svc["script"]
141
146
  else:
142
147
  fdl_directory = os.path.dirname(fdl_path)
143
148
  script_path = os.path.join(fdl_directory, svc['script'])
144
149
  with open(script_path) as s:
145
150
  svc["script"] = s.read()
146
- except IOError as e:
151
+ except IOError:
147
152
  raise Exception("Couldn't read script")
148
153
 
149
154
  # cpu parameter has to be string on the request
150
- if type(svc["cpu"]) is int or type(svc["cpu"]) is float: svc["cpu"] = str(svc["cpu"])
155
+ if type(svc["cpu"]) is int or type(svc["cpu"]) is float:
156
+ svc["cpu"] = str(svc["cpu"])
151
157
 
152
158
  except ValueError as err:
153
159
  print(err)
@@ -226,3 +232,111 @@ class Client(DefaultClient):
226
232
  """ Remove all service jobs """
227
233
  def remove_all_jobs(self, svc):
228
234
  return utils.make_request(self, _LOGS_PATH+"/"+svc, _DELETE)
235
+
236
+ """ Check cluster health """
237
+ def health_check(self):
238
+ return utils.make_request(self, _HEALTH_PATH, _GET)
239
+
240
+ """ Get deployment status of a service """
241
+ def get_deployment_status(self, name):
242
+ return utils.make_request(self, _SVC_PATH + "/" + name + "/deployment", _GET)
243
+
244
+ """ Get deployment logs of a service """
245
+ def get_deployment_logs(self, name):
246
+ return utils.make_request(self, _SVC_PATH + "/" + name + "/deployment/logs", _GET)
247
+
248
+ """ List all managed volumes """
249
+ def list_volumes(self):
250
+ return utils.make_request(self, _VOLUMES_PATH, _GET)
251
+
252
+ """ Create a new managed volume """
253
+ def create_volume(self, name, size):
254
+ data = json.dumps({"name": name, "size": size})
255
+ return utils.make_request(self, _VOLUMES_PATH, _POST, data=data)
256
+
257
+ """ Get a specific managed volume """
258
+ def get_volume(self, name):
259
+ return utils.make_request(self, _VOLUMES_PATH + "/" + name, _GET)
260
+
261
+ """ Delete a managed volume """
262
+ def delete_volume(self, name):
263
+ return utils.make_request(self, _VOLUMES_PATH + "/" + name, _DELETE)
264
+
265
+ """ Create a bucket """
266
+ def create_bucket(self, name, visibility="private", allowed_users=None):
267
+ data = json.dumps({
268
+ "bucket_name": name,
269
+ "visibility": visibility,
270
+ "allowed_users": allowed_users or []
271
+ })
272
+ return utils.make_request(self, _BUCKETS_PATH, _POST, data=data)
273
+
274
+ """ Update a bucket """
275
+ def update_bucket(self, name, visibility, allowed_users=None):
276
+ data = json.dumps({
277
+ "bucket_name": name,
278
+ "visibility": visibility,
279
+ "allowed_users": allowed_users or []
280
+ })
281
+ return utils.make_request(self, _BUCKETS_PATH, _PUT, data=data)
282
+
283
+ """ List all buckets """
284
+ def list_buckets(self):
285
+ return utils.make_request(self, _BUCKETS_PATH, _GET)
286
+
287
+ """ Get a specific bucket """
288
+ def get_bucket(self, name):
289
+ return utils.make_request(self, _BUCKETS_PATH + "/" + name, _GET)
290
+
291
+ """ Delete a bucket """
292
+ def delete_bucket(self, name):
293
+ return utils.make_request(self, _BUCKETS_PATH + "/" + name, _DELETE)
294
+
295
+ """ Get a presigned URL for a bucket file """
296
+ def presign_bucket(self, name, object_key, operation="download", expires=0, content_type="", extra_headers=None):
297
+ path = _BUCKETS_PATH + "/" + name + "/presign"
298
+ data = json.dumps({
299
+ "object_key": object_key,
300
+ "operation": operation,
301
+ "expires": expires,
302
+ "content_type": content_type,
303
+ "extra_headers": extra_headers or {},
304
+ })
305
+ return utils.make_request(self, path, _POST, data=data)
306
+
307
+ """ Get system logs (admin only) """
308
+ def get_system_logs(self, timestamps=False, previous=False):
309
+ path = _LOGS_PATH
310
+ params = []
311
+ if timestamps:
312
+ params.append("timestamps=true")
313
+ if previous:
314
+ params.append("previous=true")
315
+ if params:
316
+ path += "?" + "&".join(params)
317
+ return utils.make_request(self, path, _GET)
318
+
319
+ """ Get metrics summary """
320
+ def get_metrics_summary(self):
321
+ return utils.make_request(self, _METRICS_PATH, _GET)
322
+
323
+ """ Get metrics breakdown """
324
+ def get_metrics_breakdown(self, group_by="service"):
325
+ return utils.make_request(self, _METRICS_PATH + "/breakdown?group_by=" + group_by, _GET)
326
+
327
+ """ Get metrics for a specific service """
328
+ def get_service_metrics(self, service_name):
329
+ return utils.make_request(self, _METRICS_PATH + "/" + service_name, _GET)
330
+
331
+ """ Get own quota """
332
+ def get_own_quota(self):
333
+ return utils.make_request(self, _QUOTAS_USER_PATH, _GET)
334
+
335
+ """ Get quota for a specific user """
336
+ def get_user_quota(self, user_id):
337
+ return utils.make_request(self, _QUOTAS_USER_PATH + "/" + user_id, _GET)
338
+
339
+ """ Update quota for a user """
340
+ def update_user_quota(self, user_id, cpu, memory):
341
+ data = json.dumps({"cpu": cpu, "memory": memory})
342
+ return utils.make_request(self, _QUOTAS_USER_PATH + "/" + user_id, _PUT, data=data)
@@ -1,6 +1,6 @@
1
1
  from client import Client
2
2
 
3
- client = Client("oscar-gpu-cluster","https://focused-boyd8.im.grycap.net", "oscar", "oscar123", True)
3
+ client = Client("oscar-gpu-cluster", "https://focused-boyd8.im.grycap.net", "oscar", "oscar123", True)
4
4
 
5
5
  res = client.remove_service("cowsay")
6
6
  if res:
@@ -31,10 +31,10 @@ _SVC_PATH = "/system/services"
31
31
 
32
32
  # TODO check returns from functions
33
33
  class Storage:
34
- def __init__(self, client_obj, svc_name = None) -> None:
34
+ def __init__(self, client_obj, svc_name=None) -> None:
35
35
  self.client_obj = client_obj
36
36
  self.storage_providers = {}
37
- if svc_name != None:
37
+ if svc_name is not None:
38
38
  self.svc_name = svc_name
39
39
  self._store_provider_from_service()
40
40
  self._store_default_minio_provider()
@@ -46,13 +46,12 @@ class Storage:
46
46
 
47
47
  """ Function to store the user credentials for the default MinIO provider """
48
48
  def _store_default_minio_provider(self):
49
- config = utils.make_request(self.client_obj, _CONFIG_PATH + "/" , _GET)
49
+ config = utils.make_request(self.client_obj, _CONFIG_PATH + "/", _GET)
50
50
  if _MINIO in self.storage_providers:
51
51
  self.storage_providers[_MINIO]["default"] = json.loads(config.text)["minio_provider"]
52
52
  else:
53
53
  default = {"default": json.loads(config.text)["minio_provider"]}
54
54
  self.storage_providers[_MINIO] = default
55
-
56
55
 
57
56
  """ Function to retreive credentials of a specific storage provider """
58
57
  def _get_provider_creds(self, provider, provider_name):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oscar-python
3
- Version: 1.3.4
3
+ Version: 2.0.0
4
4
  Summary: Python client for OSCAR clusters
5
5
  Author: GRyCAP - I3M - UPV
6
6
  License: Apache-2.0
@@ -0,0 +1,324 @@
1
+ import pytest
2
+ import json
3
+ from unittest.mock import patch, mock_open
4
+ from oscar_python.client import Client
5
+
6
+
7
+ @pytest.fixture
8
+ def options():
9
+ return {
10
+ 'cluster_id': 'test_cluster',
11
+ 'endpoint': 'http://test.endpoint',
12
+ 'user': 'test_user',
13
+ 'password': 'test_password',
14
+ 'ssl': True,
15
+ 'shortname': 'test_shortname',
16
+ 'oidc_token': 'test_oidc_token'
17
+ }
18
+
19
+
20
+ def test_basic_auth_client(options):
21
+ client = Client(options)
22
+ assert client.id == options['cluster_id']
23
+ assert client.endpoint == options['endpoint']
24
+ assert client.user == options['user']
25
+ assert client.password == options['password']
26
+ assert client.ssl == options['ssl']
27
+
28
+
29
+ def test_oidc_agent_client(options):
30
+ del options['user']
31
+ client = Client(options)
32
+ assert client.id == options['cluster_id']
33
+ assert client.endpoint == options['endpoint']
34
+ assert client.shortname == options['shortname']
35
+ assert client.ssl == options['ssl']
36
+
37
+
38
+ def test_oidc_client(options):
39
+ del options['user']
40
+ del options['shortname']
41
+ client = Client(options)
42
+ assert client.id == options['cluster_id']
43
+ assert client.endpoint == options['endpoint']
44
+ assert client.oidc_token == options['oidc_token']
45
+ assert client.ssl == options['ssl']
46
+
47
+ del options['oidc_token']
48
+ options['refresh_token'] = 'test_refresh_token'
49
+ options['scopes'] = ['openid', 'profile', 'email']
50
+ options['token_endpoint'] = 'test_token_endpoint'
51
+ client = Client(options)
52
+ assert client.refresh_token == options['refresh_token']
53
+ assert client.scopes == ['openid', 'profile', 'email']
54
+ assert client.token_endpoint == options['token_endpoint']
55
+
56
+
57
+ def test_set_auth_type(options):
58
+ client = Client(options)
59
+ assert client._AUTH_TYPE == "basicauth"
60
+
61
+ del options['user']
62
+ client = Client(options)
63
+ assert client._AUTH_TYPE == "oidc-agent"
64
+
65
+ del options['shortname']
66
+ options['oidc_token'] = 'test_oidc_token'
67
+ client = Client(options)
68
+ assert client._AUTH_TYPE == "oidc"
69
+
70
+
71
+ def test_get_cluster_info(options):
72
+ client = Client(options)
73
+ with patch('oscar_python._utils.make_request') as mock_request:
74
+ client.get_cluster_info()
75
+ mock_request.assert_called_once_with(client, "/system/info", "get")
76
+
77
+
78
+ def test_get_cluster_config(options):
79
+ client = Client(options)
80
+ with patch('oscar_python._utils.make_request') as mock_request:
81
+ client.get_cluster_config()
82
+ mock_request.assert_called_once_with(client, "/system/config", "get")
83
+
84
+
85
+ def test_list_services(options):
86
+ client = Client(options)
87
+ with patch('oscar_python._utils.make_request') as mock_request:
88
+ client.list_services()
89
+ mock_request.assert_called_once_with(client, "/system/services", "get")
90
+
91
+
92
+ def test_get_service(options):
93
+ client = Client(options)
94
+ with patch('oscar_python._utils.make_request') as mock_request:
95
+ client.get_service("test_service")
96
+ mock_request.assert_called_once_with(client, "/system/services/test_service", "get")
97
+
98
+
99
+ def test_create_service_from_dict(options):
100
+ client = Client(options)
101
+ service_definition = {"name": "test_service"}
102
+ with patch('oscar_python._utils.make_request') as mock_request:
103
+ client.create_service(service_definition)
104
+ mock_request.assert_called_with(client, "/system/services", "post",
105
+ data=json.dumps(service_definition))
106
+
107
+
108
+ def test_create_service_from_file(options):
109
+ client = Client(options)
110
+ service_definition = (
111
+ "functions:\n oscar:\n - test_cluster:\n"
112
+ " name: test_service\n"
113
+ " script: test_script\n"
114
+ " cpu: 1"
115
+ )
116
+ service_file = "path/to/service.yaml"
117
+ with patch('os.path.isfile', return_value=True), \
118
+ patch('builtins.open', mock_open(read_data=service_definition)), \
119
+ patch('oscar_python._utils.make_request') as mock_request:
120
+ client.create_service(service_file)
121
+ assert mock_request.call_args[0][0] == client
122
+ assert mock_request.call_args[0][1] == "/system/services"
123
+ assert mock_request.call_args[0][2] == "post"
124
+ assert json.loads(mock_request.call_args[1]['data']) == {"name": "test_service",
125
+ "cpu": "1",
126
+ "script": service_definition}
127
+
128
+
129
+ def test_update_service_from_dict(options):
130
+ client = Client(options)
131
+ new_service = {"name": "test_service"}
132
+ with patch('oscar_python._utils.make_request') as mock_request:
133
+ mock_request.return_value.status_code = 200
134
+ client.update_service("test_service", new_service)
135
+ mock_request.assert_called_with(client, "/system/services",
136
+ "put", data=json.dumps(new_service))
137
+
138
+
139
+ def test_remove_service(options):
140
+ client = Client(options)
141
+ with patch('oscar_python._utils.make_request') as mock_request:
142
+ client.remove_service("test_service")
143
+ mock_request.assert_called_once_with(client, "/system/services/test_service", "delete")
144
+
145
+
146
+ def test_health_check(options):
147
+ client = Client(options)
148
+ with patch('oscar_python._utils.make_request') as mock_request:
149
+ client.health_check()
150
+ mock_request.assert_called_once_with(client, "/health", "get")
151
+
152
+
153
+ def test_get_deployment_status(options):
154
+ client = Client(options)
155
+ with patch('oscar_python._utils.make_request') as mock_request:
156
+ client.get_deployment_status("test_service")
157
+ mock_request.assert_called_once_with(client, "/system/services/test_service/deployment", "get")
158
+
159
+
160
+ def test_get_deployment_logs(options):
161
+ client = Client(options)
162
+ with patch('oscar_python._utils.make_request') as mock_request:
163
+ client.get_deployment_logs("test_service")
164
+ mock_request.assert_called_once_with(client, "/system/services/test_service/deployment/logs", "get")
165
+
166
+
167
+ def test_list_volumes(options):
168
+ client = Client(options)
169
+ with patch('oscar_python._utils.make_request') as mock_request:
170
+ client.list_volumes()
171
+ mock_request.assert_called_once_with(client, "/system/volumes", "get")
172
+
173
+
174
+ def test_create_volume(options):
175
+ client = Client(options)
176
+ with patch('oscar_python._utils.make_request') as mock_request:
177
+ client.create_volume("test_vol", "1Gi")
178
+ mock_request.assert_called_once_with(client, "/system/volumes", "post",
179
+ data=json.dumps({"name": "test_vol", "size": "1Gi"}))
180
+
181
+
182
+ def test_get_volume(options):
183
+ client = Client(options)
184
+ with patch('oscar_python._utils.make_request') as mock_request:
185
+ client.get_volume("test_vol")
186
+ mock_request.assert_called_once_with(client, "/system/volumes/test_vol", "get")
187
+
188
+
189
+ def test_delete_volume(options):
190
+ client = Client(options)
191
+ with patch('oscar_python._utils.make_request') as mock_request:
192
+ client.delete_volume("test_vol")
193
+ mock_request.assert_called_once_with(client, "/system/volumes/test_vol", "delete")
194
+
195
+
196
+ def test_list_buckets(options):
197
+ client = Client(options)
198
+ with patch('oscar_python._utils.make_request') as mock_request:
199
+ client.list_buckets()
200
+ mock_request.assert_called_once_with(client, "/system/buckets", "get")
201
+
202
+
203
+ def test_get_bucket(options):
204
+ client = Client(options)
205
+ with patch('oscar_python._utils.make_request') as mock_request:
206
+ client.get_bucket("test_bucket")
207
+ mock_request.assert_called_once_with(client, "/system/buckets/test_bucket", "get")
208
+
209
+
210
+ def test_create_bucket(options):
211
+ client = Client(options)
212
+ with patch('oscar_python._utils.make_request') as mock_request:
213
+ client.create_bucket("test_bucket")
214
+ mock_request.assert_called_once_with(
215
+ client, "/system/buckets", "post",
216
+ data=json.dumps({"bucket_name": "test_bucket",
217
+ "visibility": "private",
218
+ "allowed_users": []}))
219
+
220
+
221
+ def test_create_bucket_with_visibility(options):
222
+ client = Client(options)
223
+ with patch('oscar_python._utils.make_request') as mock_request:
224
+ client.create_bucket("test_bucket", visibility="public", allowed_users=["user1"])
225
+ mock_request.assert_called_once_with(
226
+ client, "/system/buckets", "post",
227
+ data=json.dumps({"bucket_name": "test_bucket",
228
+ "visibility": "public",
229
+ "allowed_users": ["user1"]}))
230
+
231
+
232
+ def test_update_bucket(options):
233
+ client = Client(options)
234
+ with patch('oscar_python._utils.make_request') as mock_request:
235
+ client.update_bucket("test_bucket", "public", ["user1"])
236
+ mock_request.assert_called_once_with(
237
+ client, "/system/buckets", "put",
238
+ data=json.dumps({"bucket_name": "test_bucket",
239
+ "visibility": "public",
240
+ "allowed_users": ["user1"]}))
241
+
242
+
243
+ def test_delete_bucket(options):
244
+ client = Client(options)
245
+ with patch('oscar_python._utils.make_request') as mock_request:
246
+ client.delete_bucket("test_bucket")
247
+ mock_request.assert_called_once_with(client, "/system/buckets/test_bucket", "delete")
248
+
249
+
250
+ def test_presign_bucket(options):
251
+ client = Client(options)
252
+ with patch('oscar_python._utils.make_request') as mock_request:
253
+ client.presign_bucket("test_bucket", "file.txt", operation="upload", expires=3600)
254
+ mock_request.assert_called_once_with(
255
+ client, "/system/buckets/test_bucket/presign", "post",
256
+ data=json.dumps({"object_key": "file.txt",
257
+ "operation": "upload",
258
+ "expires": 3600,
259
+ "content_type": "",
260
+ "extra_headers": {}}))
261
+
262
+
263
+ def test_get_system_logs(options):
264
+ client = Client(options)
265
+ with patch('oscar_python._utils.make_request') as mock_request:
266
+ client.get_system_logs()
267
+ mock_request.assert_called_once_with(client, "/system/logs", "get")
268
+
269
+
270
+ def test_get_system_logs_with_flags(options):
271
+ client = Client(options)
272
+ with patch('oscar_python._utils.make_request') as mock_request:
273
+ client.get_system_logs(timestamps=True, previous=True)
274
+ mock_request.assert_called_once_with(client, "/system/logs?timestamps=true&previous=true", "get")
275
+
276
+
277
+ def test_get_metrics_summary(options):
278
+ client = Client(options)
279
+ with patch('oscar_python._utils.make_request') as mock_request:
280
+ client.get_metrics_summary()
281
+ mock_request.assert_called_once_with(client, "/system/metrics", "get")
282
+
283
+
284
+ def test_get_metrics_breakdown(options):
285
+ client = Client(options)
286
+ with patch('oscar_python._utils.make_request') as mock_request:
287
+ client.get_metrics_breakdown("user")
288
+ mock_request.assert_called_once_with(client, "/system/metrics/breakdown?group_by=user", "get")
289
+
290
+
291
+ def test_get_metrics_breakdown_default(options):
292
+ client = Client(options)
293
+ with patch('oscar_python._utils.make_request') as mock_request:
294
+ client.get_metrics_breakdown()
295
+ mock_request.assert_called_once_with(client, "/system/metrics/breakdown?group_by=service", "get")
296
+
297
+
298
+ def test_get_service_metrics(options):
299
+ client = Client(options)
300
+ with patch('oscar_python._utils.make_request') as mock_request:
301
+ client.get_service_metrics("test_service")
302
+ mock_request.assert_called_once_with(client, "/system/metrics/test_service", "get")
303
+
304
+
305
+ def test_get_own_quota(options):
306
+ client = Client(options)
307
+ with patch('oscar_python._utils.make_request') as mock_request:
308
+ client.get_own_quota()
309
+ mock_request.assert_called_once_with(client, "/system/quotas/user", "get")
310
+
311
+
312
+ def test_get_user_quota(options):
313
+ client = Client(options)
314
+ with patch('oscar_python._utils.make_request') as mock_request:
315
+ client.get_user_quota("test_user")
316
+ mock_request.assert_called_once_with(client, "/system/quotas/user/test_user", "get")
317
+
318
+
319
+ def test_update_user_quota(options):
320
+ client = Client(options)
321
+ with patch('oscar_python._utils.make_request') as mock_request:
322
+ client.update_user_quota("test_user", "2", "4Gi")
323
+ mock_request.assert_called_once_with(client, "/system/quotas/user/test_user", "put",
324
+ data=json.dumps({"cpu": "2", "memory": "4Gi"}))
@@ -25,7 +25,9 @@ def test_run_service_with_input_and_token(mock_decode_output, mock_encode_input,
25
25
  response = client.run_service("test_service", input="test_input", token="test_token", output="output_file", timeout=30)
26
26
 
27
27
  mock_encode_input.assert_called_once_with("test_input")
28
- mock_make_request.assert_called_once_with(client, _RUN_PATH+"/test_service", _POST, data="encoded_input", token="test_token", timeout=30)
28
+ mock_make_request.assert_called_once_with(
29
+ client, _RUN_PATH+"/test_service", _POST,
30
+ data="encoded_input", token="test_token", timeout=30)
29
31
  mock_decode_output.assert_called_once_with("response_text", "output_file")
30
32
  assert response == mock_response
31
33
 
@@ -41,7 +43,9 @@ def test_run_service_with_input_no_token(mock_encode_input, mock_make_request, c
41
43
  response = client.run_service("test_service", input="test_input")
42
44
 
43
45
  mock_encode_input.assert_called_once_with("test_input")
44
- mock_make_request.assert_called_once_with(client, _RUN_PATH+"/test_service", _POST, data="encoded_input", token="test_token", timeout=None)
46
+ mock_make_request.assert_called_once_with(
47
+ client, _RUN_PATH+"/test_service", _POST,
48
+ data="encoded_input", token="test_token", timeout=None)
45
49
  assert response == mock_response
46
50
 
47
51
 
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import pytest
2
3
  from unittest.mock import MagicMock, patch
3
4
  from oscar_python.storage import Storage
@@ -11,7 +12,10 @@ def mock_client_obj():
11
12
  @pytest.fixture
12
13
  def storage(mock_client_obj):
13
14
  mock_response = MagicMock()
14
- mock_response.text = '{"minio_provider": {"access_key": "key","secret_key": "secret", "endpoint": "http://test.endpoint", "region": "us-east-1", "verify": false}}'
15
+ mock_response.text = json.dumps({
16
+ "minio_provider": {"access_key": "key", "secret_key": "secret",
17
+ "endpoint": "http://test.endpoint",
18
+ "region": "us-east-1", "verify": False}})
15
19
  with patch('oscar_python._utils.make_request', return_value=mock_response):
16
20
  return Storage(mock_client_obj)
17
21
 
@@ -1,4 +1,6 @@
1
1
  import base64
2
+ import json
3
+ import pytest
2
4
  from unittest.mock import patch, MagicMock, mock_open
3
5
 
4
6
  import oscar_python._utils as utils
@@ -74,7 +76,72 @@ def test_make_request_post():
74
76
  mock_request.return_value.raise_for_status = MagicMock()
75
77
  response = utils.make_request(c, "/test", "post", data="test_data", token="test_token")
76
78
  assert response.status_code == 200
77
- mock_request.assert_called_once_with("post", "http://test.com/test", headers={"Authorization": "Bearer test_token"}, verify=True, data="test_data", timeout=60)
79
+ mock_request.assert_called_once_with(
80
+ "post", "http://test.com/test",
81
+ headers={"Authorization": "Bearer test_token"},
82
+ verify=True, data="test_data", timeout=60)
83
+
84
+
85
+ def test_load_config(tmp_path):
86
+ config_file = tmp_path / "config.json"
87
+ config_file.write_text(json.dumps({
88
+ "clusters": {
89
+ "default": {
90
+ "endpoint": "http://test.cluster",
91
+ "user": "test_user",
92
+ "password": "test_pass",
93
+ "ssl": False
94
+ }
95
+ }
96
+ }))
97
+ opts = utils.load_config(str(config_file))
98
+ assert opts["cluster_id"] == "default"
99
+ assert opts["endpoint"] == "http://test.cluster"
100
+ assert opts["user"] == "test_user"
101
+ assert opts["password"] == "test_pass"
102
+ assert opts["ssl"] is False
103
+
104
+
105
+ def test_load_config_with_oidc(tmp_path):
106
+ config_file = tmp_path / "config.json"
107
+ config_file.write_text(json.dumps({
108
+ "clusters": {
109
+ "oidc_cluster": {
110
+ "endpoint": "http://oidc.cluster",
111
+ "oidc_token": "test_token",
112
+ "ssl": True
113
+ }
114
+ }
115
+ }))
116
+ opts = utils.load_config(str(config_file), "oidc_cluster")
117
+ assert opts["cluster_id"] == "oidc_cluster"
118
+ assert opts["endpoint"] == "http://oidc.cluster"
119
+ assert "user" not in opts
120
+ assert opts["oidc_token"] == "test_token"
121
+ assert opts["ssl"] is True
122
+
123
+
124
+ def test_load_config_missing_cluster(tmp_path):
125
+ config_file = tmp_path / "config.json"
126
+ config_file.write_text(json.dumps({"clusters": {}}))
127
+ with pytest.raises(KeyError):
128
+ utils.load_config(str(config_file), "nonexistent")
129
+
130
+
131
+ def test_load_config_minimal(tmp_path):
132
+ config_file = tmp_path / "config.json"
133
+ config_file.write_text(json.dumps({
134
+ "clusters": {
135
+ "minimal": {
136
+ "endpoint": "http://minimal.cluster"
137
+ }
138
+ }
139
+ }))
140
+ opts = utils.load_config(str(config_file), "minimal")
141
+ assert opts["endpoint"] == "http://minimal.cluster"
142
+ assert opts["ssl"] is True
143
+ for key in ("user", "password", "shortname", "oidc_token", "refresh_token"):
144
+ assert key not in opts
78
145
 
79
146
 
80
147
  def test_make_request_get():
@@ -34,7 +34,7 @@ def test_webdav_download_file(webdav):
34
34
  webdav.client = MagicMock(["download_sync"])
35
35
  webdav.client.download_sync.return_value = None
36
36
 
37
- with patch("builtins.open", mock_open()) as mock_file:
37
+ with patch("builtins.open", mock_open()):
38
38
  webdav.download_file('local_path', 'remote_path/file.txt')
39
39
 
40
40
  webdav.client.download_sync.assert_called_with('remote_path/file.txt', 'local_path/file.txt')
@@ -1,138 +0,0 @@
1
- import pytest
2
- import json
3
- from unittest.mock import patch, mock_open
4
- from oscar_python.client import Client
5
-
6
-
7
- @pytest.fixture
8
- def options():
9
- return {
10
- 'cluster_id': 'test_cluster',
11
- 'endpoint': 'http://test.endpoint',
12
- 'user': 'test_user',
13
- 'password': 'test_password',
14
- 'ssl': True,
15
- 'shortname': 'test_shortname',
16
- 'oidc_token': 'test_oidc_token'
17
- }
18
-
19
-
20
- def test_basic_auth_client(options):
21
- client = Client(options)
22
- assert client.id == options['cluster_id']
23
- assert client.endpoint == options['endpoint']
24
- assert client.user == options['user']
25
- assert client.password == options['password']
26
- assert client.ssl == options['ssl']
27
-
28
-
29
- def test_oidc_agent_client(options):
30
- del options['user']
31
- client = Client(options)
32
- assert client.id == options['cluster_id']
33
- assert client.endpoint == options['endpoint']
34
- assert client.shortname == options['shortname']
35
- assert client.ssl == options['ssl']
36
-
37
-
38
- def test_oidc_client(options):
39
- del options['user']
40
- del options['shortname']
41
- client = Client(options)
42
- assert client.id == options['cluster_id']
43
- assert client.endpoint == options['endpoint']
44
- assert client.oidc_token == options['oidc_token']
45
- assert client.ssl == options['ssl']
46
-
47
- del options['oidc_token']
48
- options['refresh_token'] = 'test_refresh_token'
49
- options['scopes'] = ['openid', 'profile', 'email']
50
- options['token_endpoint'] = 'test_token_endpoint'
51
- client = Client(options)
52
- assert client.refresh_token == options['refresh_token']
53
- assert client.scopes == ['openid', 'profile', 'email']
54
- assert client.token_endpoint == options['token_endpoint']
55
-
56
-
57
- def test_set_auth_type(options):
58
- client = Client(options)
59
- assert client._AUTH_TYPE == "basicauth"
60
-
61
- del options['user']
62
- client = Client(options)
63
- assert client._AUTH_TYPE == "oidc-agent"
64
-
65
- del options['shortname']
66
- options['oidc_token'] = 'test_oidc_token'
67
- client = Client(options)
68
- assert client._AUTH_TYPE == "oidc"
69
-
70
-
71
- def test_get_cluster_info(options):
72
- client = Client(options)
73
- with patch('oscar_python._utils.make_request') as mock_request:
74
- client.get_cluster_info()
75
- mock_request.assert_called_once_with(client, "/system/info", "get")
76
-
77
-
78
- def test_get_cluster_config(options):
79
- client = Client(options)
80
- with patch('oscar_python._utils.make_request') as mock_request:
81
- client.get_cluster_config()
82
- mock_request.assert_called_once_with(client, "/system/config", "get")
83
-
84
-
85
- def test_list_services(options):
86
- client = Client(options)
87
- with patch('oscar_python._utils.make_request') as mock_request:
88
- client.list_services()
89
- mock_request.assert_called_once_with(client, "/system/services", "get")
90
-
91
-
92
- def test_get_service(options):
93
- client = Client(options)
94
- with patch('oscar_python._utils.make_request') as mock_request:
95
- client.get_service("test_service")
96
- mock_request.assert_called_once_with(client, "/system/services/test_service", "get")
97
-
98
-
99
- def test_create_service_from_dict(options):
100
- client = Client(options)
101
- service_definition = {"name": "test_service"}
102
- with patch('oscar_python._utils.make_request') as mock_request:
103
- client.create_service(service_definition)
104
- mock_request.assert_called_with(client, "/system/services", "post",
105
- data=json.dumps(service_definition))
106
-
107
-
108
- def test_create_service_from_file(options):
109
- client = Client(options)
110
- service_definition = "functions:\n oscar:\n - test_cluster:\n name: test_service\n script: test_script\n cpu: 1"
111
- service_file = "path/to/service.yaml"
112
- with patch('os.path.isfile', return_value=True), \
113
- patch('builtins.open', mock_open(read_data=service_definition)), \
114
- patch('oscar_python._utils.make_request') as mock_request:
115
- client.create_service(service_file)
116
- assert mock_request.call_args[0][0] == client
117
- assert mock_request.call_args[0][1] == "/system/services"
118
- assert mock_request.call_args[0][2] == "post"
119
- assert json.loads(mock_request.call_args[1]['data']) == {"name": "test_service",
120
- "cpu": "1",
121
- "script": service_definition}
122
-
123
-
124
- def test_update_service_from_dict(options):
125
- client = Client(options)
126
- new_service = {"name": "test_service"}
127
- with patch('oscar_python._utils.make_request') as mock_request:
128
- mock_request.return_value.status_code = 200
129
- client.update_service("test_service", new_service)
130
- mock_request.assert_called_with(client, "/system/services",
131
- "put", data=json.dumps(new_service))
132
-
133
-
134
- def test_remove_service(options):
135
- client = Client(options)
136
- with patch('oscar_python._utils.make_request') as mock_request:
137
- client.remove_service("test_service")
138
- mock_request.assert_called_once_with(client, "/system/services/test_service", "delete")
File without changes
File without changes
File without changes
File without changes
File without changes