oks-cli 1.17__tar.gz → 1.18__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 (26) hide show
  1. {oks_cli-1.17 → oks_cli-1.18}/PKG-INFO +1 -1
  2. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/cluster.py +3 -0
  3. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/profile.py +1 -1
  4. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/project.py +18 -0
  5. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/utils.py +19 -4
  6. {oks_cli-1.17 → oks_cli-1.18}/oks_cli.egg-info/PKG-INFO +1 -1
  7. {oks_cli-1.17 → oks_cli-1.18}/setup.py +1 -1
  8. {oks_cli-1.17 → oks_cli-1.18}/tests/test_profile.py +27 -1
  9. {oks_cli-1.17 → oks_cli-1.18}/tests/test_project.py +74 -0
  10. {oks_cli-1.17 → oks_cli-1.18}/LICENSE +0 -0
  11. {oks_cli-1.17 → oks_cli-1.18}/README.md +0 -0
  12. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/__init__.py +0 -0
  13. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/cache.py +0 -0
  14. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/main.py +0 -0
  15. {oks_cli-1.17 → oks_cli-1.18}/oks_cli/quotas.py +0 -0
  16. {oks_cli-1.17 → oks_cli-1.18}/oks_cli.egg-info/SOURCES.txt +0 -0
  17. {oks_cli-1.17 → oks_cli-1.18}/oks_cli.egg-info/dependency_links.txt +0 -0
  18. {oks_cli-1.17 → oks_cli-1.18}/oks_cli.egg-info/entry_points.txt +0 -0
  19. {oks_cli-1.17 → oks_cli-1.18}/oks_cli.egg-info/requires.txt +0 -0
  20. {oks_cli-1.17 → oks_cli-1.18}/oks_cli.egg-info/top_level.txt +0 -0
  21. {oks_cli-1.17 → oks_cli-1.18}/setup.cfg +0 -0
  22. {oks_cli-1.17 → oks_cli-1.18}/tests/test_cache.py +0 -0
  23. {oks_cli-1.17 → oks_cli-1.18}/tests/test_cluster.py +0 -0
  24. {oks_cli-1.17 → oks_cli-1.18}/tests/test_nodepool.py +0 -0
  25. {oks_cli-1.17 → oks_cli-1.18}/tests/test_quota.py +0 -0
  26. {oks_cli-1.17 → oks_cli-1.18}/tests/test_shell_completion.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oks-cli
3
- Version: 1.17
3
+ Version: 1.18
4
4
  Author: Outscale SAS
5
5
  Author-email: opensource@outscale.com
6
6
  License: BSD
@@ -257,6 +257,9 @@ def cluster_get_command(ctx, project_name, cluster_name, output, profile):
257
257
  def prepare_cluster_template(cluster_config):
258
258
  cluster_template = get_template("cluster")
259
259
 
260
+ if cluster_template.get("project_id") == "":
261
+ cluster_template.pop("project_id", None)
262
+
260
263
  admin_whitelist = cluster_config.get("admin_whitelist") or []
261
264
  if isinstance(admin_whitelist, str):
262
265
  admin_whitelist = [admin_whitelist]
@@ -18,7 +18,7 @@ def profile():
18
18
  @click.option('--password', required=False, help="Password", type=click.STRING)
19
19
  @click.option('--region', required=True, help="Region name", type=click.Choice(['eu-west-2', 'cloudgouv-eu-west-1']))
20
20
  @click.option('--endpoint', required=False, help="API endpoint", type=click.STRING)
21
- @click.option('--jwt', help="Enable JWT, by default is false")
21
+ @click.option('--jwt', help="Enable JWT, by default is false", type=click.BOOL)
22
22
  def add_profile(profile_name, access_key, secret_key, username, password, region, endpoint, jwt):
23
23
  """Add a new profile with AK/SK or username/password authentication."""
24
24
  if not profile_name:
@@ -391,4 +391,22 @@ def project_get_public_ips(ctx, project_name, output, profile):
391
391
  project_id = find_project_id_by_name(project_name)
392
392
 
393
393
  data = do_request("GET", f'projects/{project_id}/public_ips')
394
+ print_output(data, output)
395
+
396
+
397
+
398
+ # GET NETS BY PROJECT NAME
399
+ @project.command('nets', help="Get project nets")
400
+ @click.option('--project-name', '-p', help="Name of the project", shell_complete=project_completer)
401
+ @click.option('--output', '-o', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
402
+ @click.option('--profile',help="Configuration profile to use")
403
+ @click.pass_context
404
+ def project_get_public_ips(ctx, project_name, output, profile):
405
+ """Retrieve the list of Nets associated with the specified project."""
406
+ project_name, _, profile = ctx_update(ctx, project_name, None, profile)
407
+ login_profile(profile)
408
+
409
+ project_id = find_project_id_by_name(project_name)
410
+
411
+ data = do_request("GET", f'projects/{project_id}/nets')
394
412
  print_output(data, output)
@@ -77,6 +77,8 @@ def find_response_object(data):
77
77
  return response["PublicIps"]
78
78
  elif key == "IP":
79
79
  return response["IP"]
80
+ elif key == "Nets":
81
+ return response["Nets"]
80
82
 
81
83
  raise click.ClickException("The API response format is incorrect.")
82
84
 
@@ -108,10 +110,13 @@ def do_request(method, path, *args, **kwargs):
108
110
  obj = find_response_object(data)
109
111
  return obj
110
112
  except requests.exceptions.HTTPError as err:
111
- otp_response = handle_otp_error(err, lambda: do_request(method, path, *args, **kwargs))
113
+ otp_response = handle_otp_error(err, method, path, args, kwargs)
112
114
  if otp_response is not None:
113
115
  return otp_response
114
116
 
117
+ if os.environ.get("OKS_OTP_CODE") is not None:
118
+ raise JSONClickException(err.response.text)
119
+
115
120
  jwt_response = handle_jwt_error(err, method, path, args, kwargs)
116
121
  if jwt_response is not None:
117
122
  return jwt_response
@@ -210,7 +215,7 @@ def print_table(data, table_fields, align="l", style=None):
210
215
  table.add_row([d[v] if v in d else "" for v in values])
211
216
  click.echo(table)
212
217
 
213
- def handle_otp_error(err, callback):
218
+ def handle_otp_error(err, method, path, args, kwargs):
214
219
  """Handle OTP authentication error by prompting the user and retrying the request."""
215
220
  try:
216
221
  response_body = json.loads(err.response.text)
@@ -218,7 +223,11 @@ def handle_otp_error(err, callback):
218
223
  otp_code = click.prompt('Enter your OTP code', type=int)
219
224
  os.environ["OKS_OTP_CODE"] = str(otp_code)
220
225
 
221
- return callback()
226
+ logging.info("Retrying request with user-provided OTP...")
227
+ return do_request(method, path, *args, **kwargs)
228
+
229
+ except JSONClickException:
230
+ raise
222
231
  except Exception:
223
232
  return None
224
233
 
@@ -257,8 +266,14 @@ def find_cluster_id_by_name(project_id, cluster_name):
257
266
  """Retrieve the cluster ID by name within a given project, or use the default cluster if none is provided."""
258
267
  if not cluster_name:
259
268
  cluster_id = get_cluster_id()
269
+ errors = {"Error": "--cluster-name must be specified, or a default cluster must be set"}
270
+
260
271
  if not cluster_id:
261
- raise click.BadParameter("--cluster-name must be specified, or a default cluster must be set")
272
+ raise JSONClickException(json.dumps(errors))
273
+
274
+ cluster = do_request("GET", f'clusters/{cluster_id}')
275
+ if cluster['project_id'] != project_id:
276
+ raise JSONClickException(json.dumps(errors))
262
277
  else:
263
278
  data = do_request("GET", 'clusters', params={"project_id": project_id, "name": cluster_name})
264
279
  if len(data) != 1:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oks-cli
3
- Version: 1.17
3
+ Version: 1.18
4
4
  Author: Outscale SAS
5
5
  Author-email: opensource@outscale.com
6
6
  License: BSD
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="oks-cli",
5
- version="1.17",
5
+ version="1.18",
6
6
  packages=['oks_cli'],
7
7
  author="Outscale SAS",
8
8
  author_email="opensource@outscale.com",
@@ -1,6 +1,6 @@
1
1
  from click.testing import CliRunner
2
2
  from oks_cli.main import cli
3
-
3
+ from unittest.mock import patch
4
4
 
5
5
  def test_profile_list_command():
6
6
  runner = CliRunner()
@@ -38,6 +38,32 @@ def test_profile_add_command():
38
38
  assert result.exit_code == 0
39
39
  assert "Profile default has been successfully added" in result.output
40
40
 
41
+ def test_profile_add_jwt_boolean():
42
+ runner = CliRunner()
43
+
44
+ with patch("oks_cli.profile.set_profile") as mock_set_profile:
45
+ result = runner.invoke(
46
+ cli,
47
+ [
48
+ "profile", "add",
49
+ "--region", "eu-west-2",
50
+ "--access-key", "AK",
51
+ "--secret-key", "SK",
52
+ "--jwt", "true"
53
+ ],
54
+ input="y\n"
55
+ )
56
+
57
+ assert result.exit_code == 0
58
+
59
+ mock_set_profile.assert_called_once()
60
+ profile_name, obj = mock_set_profile.call_args[0]
61
+
62
+ assert profile_name == "default"
63
+
64
+ assert isinstance(obj["jwt"], bool)
65
+ assert obj["jwt"] is True
66
+
41
67
  def test_profile_update_command(add_default_profile):
42
68
  runner = CliRunner()
43
69
  result = runner.invoke(cli, ["profile", "update", "--profile-name", "default", "--region", "cloudgouv-eu-west-1"])
@@ -476,6 +476,80 @@ def test_project_publicips_yaml(mock_request, add_default_profile):
476
476
  # END PROJECT PUBLICIPS COMMAND
477
477
 
478
478
 
479
+ # START PROJECT NETS COMMAND
480
+ # Test the "project nets" command: verifies fetching project nets
481
+ nets = [{
482
+ "DhcpOptionsSetId": "dopt-12345678",
483
+ "IpRange": "10.50.0.0/16",
484
+ "NetId": "vpc-12345678",
485
+ "State": "available",
486
+ "Tags": [
487
+ {
488
+ "Key": "Name",
489
+ "Value": "default"
490
+ }
491
+ ]
492
+ }]
493
+
494
+ @patch("oks_cli.utils.requests.request")
495
+ def test_project_nets_command(mock_request, add_default_profile):
496
+ mock_request.side_effect = [
497
+ MagicMock(status_code=200, headers = {}, json=lambda: {"ResponseContext": {}, "Projects": [{"id": "12345"}]}),
498
+ MagicMock(status_code=200, headers = {}, json=lambda: {"ResponseContext": {}, "Nets": nets })
499
+ ]
500
+
501
+ runner = CliRunner()
502
+ result = runner.invoke(cli, ["project", "nets", "-p", "test"])
503
+ assert result.exit_code == 0
504
+
505
+ data = json.loads(result.output)
506
+ assert isinstance(data, list)
507
+ assert data == nets
508
+
509
+ @patch("oks_cli.utils.requests.request")
510
+ def test_project_nets_json(mock_request, add_default_profile):
511
+ mock_request.side_effect = [
512
+ MagicMock(status_code=200, headers={}, json=lambda: {"ResponseContext": {}, "Projects": [{"id": "12345"}]}),
513
+ MagicMock(status_code=200, headers = {}, json=lambda: {"ResponseContext": {}, "Nets": nets })
514
+ ]
515
+
516
+ runner = CliRunner()
517
+ result = runner.invoke(cli, [
518
+ "project", "nets",
519
+ "-p", "test-project",
520
+ "-o", "json",
521
+ "--profile", "default"
522
+ ])
523
+
524
+ assert result.exit_code == 0
525
+
526
+ data = json.loads(result.output)
527
+ assert isinstance(data, list)
528
+ assert data == nets
529
+
530
+ @patch("oks_cli.utils.requests.request")
531
+ def test_project_nets_yaml(mock_request, add_default_profile):
532
+ mock_request.side_effect = [
533
+ MagicMock(status_code=200, headers={}, json=lambda: {"ResponseContext": {}, "Projects": [{"id": "12345"}]}),
534
+ MagicMock(status_code=200, headers = {}, json=lambda: {"ResponseContext": {}, "Nets": nets })
535
+ ]
536
+
537
+ runner = CliRunner()
538
+ result = runner.invoke(cli, [
539
+ "project", "nets",
540
+ "-p", "test-project",
541
+ "-o", "yaml",
542
+ "--profile", "default"
543
+ ])
544
+
545
+ assert result.exit_code == 0
546
+
547
+ data = yaml.safe_load(result.output)
548
+ assert isinstance(data, list)
549
+ assert data == nets
550
+ # END PROJECT NETS COMMAND
551
+
552
+
479
553
  # Test the "project list" command with --watch option
480
554
  @patch("oks_cli.utils.requests.request")
481
555
  @patch("time.sleep")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes