oks-cli 1.14__tar.gz → 1.15__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 (31) hide show
  1. {oks_cli-1.14 → oks_cli-1.15}/PKG-INFO +14 -19
  2. {oks_cli-1.14 → oks_cli-1.15}/README.md +13 -18
  3. {oks_cli-1.14 → oks_cli-1.15}/oks_cli/cache.py +5 -5
  4. {oks_cli-1.14 → oks_cli-1.15}/oks_cli/cluster.py +29 -25
  5. {oks_cli-1.14 → oks_cli-1.15}/oks_cli/main.py +4 -4
  6. {oks_cli-1.14 → oks_cli-1.15}/oks_cli/profile.py +57 -15
  7. {oks_cli-1.14 → oks_cli-1.15}/oks_cli/project.py +24 -13
  8. oks_cli-1.15/oks_cli/quotas.py +21 -0
  9. {oks_cli-1.14 → oks_cli-1.15}/oks_cli/utils.py +136 -24
  10. {oks_cli-1.14 → oks_cli-1.15}/oks_cli.egg-info/PKG-INFO +14 -19
  11. {oks_cli-1.14 → oks_cli-1.15}/oks_cli.egg-info/SOURCES.txt +2 -1
  12. {oks_cli-1.14 → oks_cli-1.15}/setup.py +1 -1
  13. oks_cli-1.15/tests/test_cluster.py +404 -0
  14. {oks_cli-1.14 → oks_cli-1.15}/tests/test_nodepool.py +1 -1
  15. oks_cli-1.15/tests/test_profile.py +58 -0
  16. oks_cli-1.15/tests/test_project.py +457 -0
  17. oks_cli-1.15/tests/test_quota.py +27 -0
  18. oks_cli-1.15/tests/test_shell_completion.py +104 -0
  19. oks_cli-1.14/oks_cli/quotas.py +0 -14
  20. oks_cli-1.14/tests/test_cluster.py +0 -158
  21. oks_cli-1.14/tests/test_profile.py +0 -30
  22. oks_cli-1.14/tests/test_project.py +0 -102
  23. oks_cli-1.14/tests/test_quota.py +0 -15
  24. {oks_cli-1.14 → oks_cli-1.15}/LICENSE +0 -0
  25. {oks_cli-1.14 → oks_cli-1.15}/oks_cli/__init__.py +0 -0
  26. {oks_cli-1.14 → oks_cli-1.15}/oks_cli.egg-info/dependency_links.txt +0 -0
  27. {oks_cli-1.14 → oks_cli-1.15}/oks_cli.egg-info/entry_points.txt +0 -0
  28. {oks_cli-1.14 → oks_cli-1.15}/oks_cli.egg-info/requires.txt +0 -0
  29. {oks_cli-1.14 → oks_cli-1.15}/oks_cli.egg-info/top_level.txt +0 -0
  30. {oks_cli-1.14 → oks_cli-1.15}/setup.cfg +0 -0
  31. {oks_cli-1.14 → oks_cli-1.15}/tests/test_cache.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oks-cli
3
- Version: 1.14
3
+ Version: 1.15
4
4
  Author: Outscale SAS
5
5
  Author-email: opensource@outscale.com
6
6
  License: BSD
@@ -102,31 +102,17 @@ Dynamic: requires-dist
102
102
  ### Standard Installation
103
103
 
104
104
  ```bash
105
- # Clone the repository
106
- git clone https://github.com/outscale/oks-cli.git
107
- cd oks-cli
108
-
109
105
  # Create and activate a virtual environment
110
106
  python -m venv venv
111
107
  source venv/bin/activate
112
108
 
113
- # Install dependencies
114
- pip install -r requirements.txt
115
-
116
- # Install the CLI in editable mode
117
- pip install -e .
118
- ```
119
-
120
- ### User Installation
109
+ # Install the CLI
110
+ pip install oks-cli
121
111
 
122
- Install globally without a virtual environment (Python 3.11):
123
-
124
- ```bash
125
- pip3.11 install -e --user .
112
+ # Check version of oks-cli
113
+ oks-cli version
126
114
  ```
127
115
 
128
- > **Note:** Ensure `~/Library/Python/3.11/bin` (macOS) or the equivalent path is in your `PATH`.
129
-
130
116
  ---
131
117
 
132
118
  ## 🚀 Usage
@@ -207,6 +193,15 @@ oks-cli project login --project-name my-project
207
193
  Install the CLI in editable mode with development dependencies
208
194
 
209
195
  ```bash
196
+ # Clone the repository
197
+ git clone https://github.com/outscale/oks-cli.git
198
+ cd oks-cli
199
+
200
+ # Create and activate a virtual environment
201
+ python -m venv venv
202
+ source venv/bin/activate
203
+
204
+ # CLI in editable mode
210
205
  pip install -e ".[dev]"
211
206
  ```
212
207
 
@@ -59,31 +59,17 @@
59
59
  ### Standard Installation
60
60
 
61
61
  ```bash
62
- # Clone the repository
63
- git clone https://github.com/outscale/oks-cli.git
64
- cd oks-cli
65
-
66
62
  # Create and activate a virtual environment
67
63
  python -m venv venv
68
64
  source venv/bin/activate
69
65
 
70
- # Install dependencies
71
- pip install -r requirements.txt
72
-
73
- # Install the CLI in editable mode
74
- pip install -e .
75
- ```
76
-
77
- ### User Installation
66
+ # Install the CLI
67
+ pip install oks-cli
78
68
 
79
- Install globally without a virtual environment (Python 3.11):
80
-
81
- ```bash
82
- pip3.11 install -e --user .
69
+ # Check version of oks-cli
70
+ oks-cli version
83
71
  ```
84
72
 
85
- > **Note:** Ensure `~/Library/Python/3.11/bin` (macOS) or the equivalent path is in your `PATH`.
86
-
87
73
  ---
88
74
 
89
75
  ## 🚀 Usage
@@ -164,6 +150,15 @@ oks-cli project login --project-name my-project
164
150
  Install the CLI in editable mode with development dependencies
165
151
 
166
152
  ```bash
153
+ # Clone the repository
154
+ git clone https://github.com/outscale/oks-cli.git
155
+ cd oks-cli
156
+
157
+ # Create and activate a virtual environment
158
+ python -m venv venv
159
+ source venv/bin/activate
160
+
161
+ # CLI in editable mode
167
162
  pip install -e ".[dev]"
168
163
  ```
169
164
 
@@ -1,11 +1,11 @@
1
1
  import click
2
- from .utils import clear_cache, find_project_id_by_name, find_cluster_id_by_name, get_all_cache, get_expiration_date, ctx_update, login_profile, profile_completer
2
+ from .utils import clear_cache, find_project_id_by_name, find_cluster_id_by_name, get_all_cache, get_expiration_date, ctx_update, login_profile, profile_completer, cluster_completer, project_completer
3
3
  import prettytable
4
4
 
5
5
  # DEFINE THE CACHE COMMAND GROUP
6
6
  @click.group(help="Cache related commands.")
7
- @click.option('--project-name', '-p', required = False, help="Project Name")
8
- @click.option('--cluster-name', '-c', required = False, help="Cluster Name")
7
+ @click.option('--project-name', '-p', required = False, help="Project Name", shell_complete=project_completer)
8
+ @click.option('--cluster-name', '-c', required = False, help="Cluster Name", shell_complete=cluster_completer)
9
9
  @click.option("--profile", help="Configuration profile to use", shell_complete=profile_completer)
10
10
  @click.pass_context
11
11
  def cache(ctx, project_name, cluster_name, profile):
@@ -20,8 +20,8 @@ def delete_cache(force):
20
20
  clear_cache()
21
21
 
22
22
  @cache.command('kubeconfigs', help="List cached kubeconfigs")
23
- @click.option('--project-name', '-p', required=False, help="Project Name")
24
- @click.option('--cluster-name', '-c', required=False, help="Cluster Name")
23
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
24
+ @click.option('--cluster-name', '-c', required=False, help="Cluster Name", shell_complete=cluster_completer)
25
25
  @click.option('--plain', is_flag=True, help="Plain table format")
26
26
  @click.option('--msword', is_flag=True, help="Microsoft Word table format")
27
27
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
@@ -12,7 +12,7 @@ import human_readable
12
12
  import prettytable
13
13
  import logging
14
14
 
15
- from .utils import do_request, print_output, find_project_id_by_name, find_cluster_id_by_name, get_cache, save_cache, detect_and_parse_input, verify_certificate, shell_completions, transform_tuple, profile_list, login_profile, cluster_create_in_background, ctx_update, set_cluster_id, get_cluster_id, get_project_id, get_template, get_cluster_name, format_changed_row, is_interesting_status, profile_completer
15
+ from .utils import cluster_completer, do_request, print_output, find_project_id_by_name, find_cluster_id_by_name, get_cache, save_cache, detect_and_parse_input, verify_certificate, shell_completions, transform_tuple, profile_list, login_profile, cluster_create_in_background, ctx_update, set_cluster_id, get_cluster_id, get_project_id, get_template, get_cluster_name, format_changed_row, is_interesting_status, profile_completer, project_completer
16
16
 
17
17
  from .profile import add_profile
18
18
  from .project import project_create, project_login
@@ -20,9 +20,9 @@ from .project import project_create, project_login
20
20
  # DEFINE THE CLUSTER GROUP
21
21
  @click.group(help="Cluster related commands.")
22
22
  @click.option('--project', 'project_name', required = False, help="Project Name")
23
- @click.option('--project-name', '-p', required = False, help="Project Name")
23
+ @click.option('--project-name', '-p', required = False, help="Project Name", shell_complete=project_completer)
24
24
  @click.option('--name', 'cluster_name', required = False, help="Cluster Name")
25
- @click.option('--cluster-name', '-c', required = False, help="Cluster Name")
25
+ @click.option('--cluster-name', '-c', required = False, help="Cluster Name", shell_complete=cluster_completer)
26
26
  @click.option("--profile", help="Configuration profile to use", shell_complete=profile_completer)
27
27
  @click.pass_context
28
28
  def cluster(ctx, project_name, cluster_name, profile):
@@ -31,7 +31,7 @@ def cluster(ctx, project_name, cluster_name, profile):
31
31
 
32
32
  # LOGIN ON CLUSTER
33
33
  @cluster.command('login', help="Set a default cluster")
34
- @click.option('--cluster-name', '-c', required=False, help="Name of cluster")
34
+ @click.option('--cluster-name', '-c', required=False, help="Name of cluster", shell_complete=cluster_completer)
35
35
  @click.option("--profile", help="Configuration profile to use", shell_complete=profile_completer)
36
36
  @click.pass_context
37
37
  def cluster_login(ctx, cluster_name, profile):
@@ -70,9 +70,9 @@ def cluster_logout(ctx, profile):
70
70
 
71
71
  # LIST CLUSTERS
72
72
  @cluster.command('list', help="List all clusters")
73
- @click.option('--project-name', '-p', required=False, help="Project Name")
73
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
74
74
  @click.option('--name', 'cluster_name', required = False, help="Cluster Name")
75
- @click.option('--cluster-name', '-c', required = False, help="Cluster Name")
75
+ @click.option('--cluster-name', '-c', required = False, help="Cluster Name", shell_complete=cluster_completer)
76
76
  @click.option('--deleted', is_flag=True, help="List deleted clusters")
77
77
  @click.option('--plain', is_flag=True, help="Plain table format")
78
78
  @click.option('--msword', is_flag=True, help="Microsoft Word table format")
@@ -215,9 +215,9 @@ def cluster_list(ctx, project_name, cluster_name, deleted, plain, msword, watch,
215
215
 
216
216
  # GET CLUSTER BY NAME
217
217
  @cluster.command('get', help="Get a cluster by name")
218
- @click.option('--project-name', '-p', required = False, help="Project Name")
219
- @click.option('--name', 'cluster_name', required=False, help="Cluster Name")
220
- @click.option('--cluster-name', '-c', required=False, help="Cluster Name")
218
+ @click.option('--project-name', '-p', required = False, help="Project Name", shell_complete=project_completer)
219
+ @click.option('--name', 'cluster_name', required=False, help="Cluster Name", shell_complete=cluster_completer)
220
+ @click.option('--cluster-name', '-c', required=False, help="Cluster Name", shell_complete=cluster_completer)
221
221
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
222
222
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
223
223
  @click.pass_context
@@ -311,8 +311,8 @@ def _create_cluster(project_name, cluster_config, output):
311
311
 
312
312
  # CLUSTER CREATE BY NAME
313
313
  @cluster.command('create', help="Create a new cluster")
314
- @click.option('--project-name', '-p', required=False, help="Project Name")
315
- @click.option('--cluster-name', '--name', '-c', required=False, help="Cluster Name")
314
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
315
+ @click.option('--cluster-name', '--name', '-c', required=False, help="Cluster Name", shell_complete=cluster_completer)
316
316
  @click.option('--description', help="Description of the cluster")
317
317
  @click.option('--admin', help="Admin Whitelist")
318
318
  @click.option('--version', shell_complete=shell_completions, help="Kubernetes version")
@@ -325,12 +325,13 @@ def _create_cluster(project_name, cluster_config, output):
325
325
  @click.option('--quirk', multiple=True, help="Quirk")
326
326
  @click.option('--tags', help="Comma-separated list of tags, example: 'key1=value1,key2=value2'")
327
327
  @click.option('--disable-api-termination', type=click.BOOL, help="Disable delete action by API")
328
+ @click.option('--cp-multi-az', is_flag=True, help="enable control plane multi az")
328
329
  @click.option('--dry-run', is_flag=True, help="Client dry-run, only print the object that would be sent, without sending it")
329
330
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
330
331
  @click.option('-f', '--filename', type=click.File("r"), help="Path to file to use to create the cluster ")
331
332
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
332
333
  @click.pass_context
333
- def cluster_create_command(ctx, project_name, cluster_name, description, admin, version, cidr_pods, cidr_service, control_plane, zone, enable_admission_plugins, disable_admission_plugins, quirk, tags, disable_api_termination, dry_run, output, filename, profile):
334
+ def cluster_create_command(ctx, project_name, cluster_name, description, admin, version, cidr_pods, cidr_service, control_plane, zone, enable_admission_plugins, disable_admission_plugins, quirk, tags, disable_api_termination, cp_multi_az, dry_run, output, filename, profile):
334
335
  """CLI command to create a new Kubernetes cluster with optional configuration parameters."""
335
336
  project_name, cluster_name, profile = ctx_update(ctx, project_name, cluster_name, profile)
336
337
  login_profile(profile)
@@ -397,6 +398,9 @@ def cluster_create_command(ctx, project_name, cluster_name, description, admin,
397
398
 
398
399
  if disable_api_termination is not None:
399
400
  cluster_config["disable_api_termination"] = disable_api_termination
401
+
402
+ if cp_multi_az is not None:
403
+ cluster_config["cp_multi_az"] = cp_multi_az
400
404
 
401
405
  if not dry_run:
402
406
  _create_cluster(project_name, cluster_config, output)
@@ -407,9 +411,9 @@ def cluster_create_command(ctx, project_name, cluster_name, description, admin,
407
411
 
408
412
  # UPDATE CLUSTER
409
413
  @cluster.command('update', help="Update a cluster by name")
410
- @click.option('--project-name', '-p', required=False, help="Project name")
414
+ @click.option('--project-name', '-p', required=False, help="Project name", shell_complete=project_completer)
411
415
  @click.option('--name', 'cluster_name', required=False, help="Cluster name")
412
- @click.option('--cluster-name', '-c', required=False, help="Cluster name")
416
+ @click.option('--cluster-name', '-c', required=False, help="Cluster name", shell_complete=cluster_completer)
413
417
  @click.option('--description', help="Description of the cluster")
414
418
  @click.option('--admin', help="Admin Whitelist")
415
419
  @click.option('--version', shell_complete=shell_completions, help="Kubernetes version")
@@ -495,9 +499,9 @@ def cluster_update_command(ctx, project_name, cluster_name, description, admin,
495
499
 
496
500
  # UPGRADE CLUSTER
497
501
  @cluster.command('upgrade', help="Upgrade a cluster by name")
498
- @click.option('--project-name', '-p', required=False, help="Project name")
502
+ @click.option('--project-name', '-p', required=False, help="Project name", shell_complete=project_completer)
499
503
  @click.option('--name', 'cluster_name', required=False, help="Cluster name")
500
- @click.option('--cluster-name', '-c', required=False, help="Cluster name")
504
+ @click.option('--cluster-name', '-c', required=False, help="Cluster name", shell_complete=cluster_completer)
501
505
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
502
506
  @click.option('--force', is_flag=True, help="Force upgrade")
503
507
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
@@ -517,9 +521,9 @@ def cluster_update_command(ctx, project_name, cluster_name, output, force, profi
517
521
 
518
522
  # DELETE CLUSTER BY NAME
519
523
  @cluster.command('delete', help="Delete a cluster by name")
520
- @click.option('--project-name', '-p', required=False, help="Project name")
524
+ @click.option('--project-name', '-p', required=False, help="Project name", shell_complete=project_completer)
521
525
  @click.option('--name', 'cluster_name', required=False, help="Cluster name")
522
- @click.option('--cluster-name', '-c', required=False, help="Cluster name")
526
+ @click.option('--cluster-name', '-c', required=False, help="Cluster name", shell_complete=cluster_completer)
523
527
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
524
528
  @click.option('--dry-run', is_flag=True, help="Run without any action")
525
529
  @click.option('--force', is_flag=True, help="Force deletion without confirmation")
@@ -549,9 +553,9 @@ def cluster_delete_command(ctx, project_name, cluster_name, output, dry_run, for
549
553
 
550
554
  # GET KUBECONFIG
551
555
  @cluster.command('kubeconfig', help="Fetch the kubeconfig for a cluster")
552
- @click.option('--project-name', '-p', required=False, help="Project Name")
556
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
553
557
  @click.option('--name', 'cluster_name', required=False, help="Cluster name")
554
- @click.option('--cluster-name', '-c', required=False, help="Cluster Name")
558
+ @click.option('--cluster-name', '-c', required=False, help="Cluster Name", shell_complete=cluster_completer)
555
559
  @click.option('--print-path', is_flag=True, help="Print path to saved kubeconfig")
556
560
  @click.option('--refresh', '--force', is_flag=True, help="Force refresh saved kubeconfig")
557
561
  @click.option('--nacl', is_flag=True, help="Use public key encryption on wire (require api support)")
@@ -659,8 +663,8 @@ def _run_kubectl(project_id, cluster_id, user, group, args, input=None):
659
663
 
660
664
 
661
665
  @cluster.command('kubectl', help='Fetch the kubeconfig for a cluster and run kubectl against it', context_settings={"ignore_unknown_options": True})
662
- @click.option('--project-name', '-p', required=False, help="Project Name")
663
- @click.option('--cluster-name', '-c', required=False, help="Cluster Name")
666
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
667
+ @click.option('--cluster-name', '-c', required=False, help="Cluster Name", shell_complete=cluster_completer)
664
668
  @click.option('--user', type=click.STRING, help="User")
665
669
  @click.option('--group', type=click.STRING, help="Group")
666
670
  @click.argument("args", nargs=-1, type=click.UNPROCESSED)
@@ -678,8 +682,8 @@ def cluster_kubectl_command(ctx, project_name, cluster_name, user, group, args,
678
682
 
679
683
 
680
684
  @click.group(help="nodepool related commands.")
681
- @click.option('--project-name', '-p', required=False, help="Project Name")
682
- @click.option('--cluster-name', '-c', required=False, help="Cluster Name")
685
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
686
+ @click.option('--cluster-name', '-c', required=False, help="Cluster Name", shell_complete=cluster_completer)
683
687
  @click.option('--user', type=click.STRING, help="User")
684
688
  @click.option('--group', type=click.STRING, help="Group")
685
689
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
@@ -711,7 +715,7 @@ def nodepool_list(ctx):
711
715
  @click.option('--nodepool-name', '-n', default="nodepool01", help="Nodepool Name")
712
716
  @click.option('--count', default=2, help="Count of nodes")
713
717
  @click.option('--type', 'vmtype', default="tinav6.c2r4p3", help="Type of VMs")
714
- @click.option('--zone', default=["eu-west-2a"], multiple=True, help="Provide zone")
718
+ @click.option('--zone', multiple=True, required=True, help="Provide zone")
715
719
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
716
720
  @click.option('--dry-run', is_flag=True, help="Run without any action")
717
721
  @click.option('-f', '--filename', type=click.File("r"), help="Path to file to use to create the Nodepool ")
@@ -9,13 +9,13 @@ from .profile import profile
9
9
  from .cache import cache
10
10
  from .quotas import quotas
11
11
 
12
- from .utils import ctx_update, login_profile, install_completions, profile_completer
12
+ from .utils import ctx_update, login_profile, install_completions, profile_completer, cluster_completer, project_completer
13
13
 
14
14
  # Main CLI entry point
15
15
  @click.group(invoke_without_command=True)
16
16
  @click.option("--profile", help="Configuration profile to use", shell_complete=profile_completer)
17
- @click.option('--project-name', '-p', required = False, help="Project Name")
18
- @click.option('--cluster-name', '-c', required = False, help="Cluster Name")
17
+ @click.option('--project-name', '-p', required = False, help="Project Name", shell_complete=project_completer)
18
+ @click.option('--cluster-name', '-c', required = False, help="Cluster Name", shell_complete=cluster_completer)
19
19
  @click.option('-v', '--verbose', count=True)
20
20
  @click.pass_context
21
21
  def cli(ctx, project_name, cluster_name, profile, verbose):
@@ -82,7 +82,7 @@ def version():
82
82
  print(importlib.metadata.version(__package__))
83
83
 
84
84
  @cli.command("install-completion", help="Install shell completion scripts.")
85
- @click.option('--type', help="Shell")
85
+ @click.option('--type', help="Shell, supported [bash,zsh]")
86
86
  def install_completion(type):
87
87
  """Install shell completion scripts for the CLI."""
88
88
  install_completions(type)
@@ -1,5 +1,6 @@
1
1
  import click
2
- from .utils import set_profile, remove_profile, profile_list, DEFAULT_API_URL, get_profiles
2
+ import prettytable
3
+ from .utils import set_profile, remove_profile, profile_list, DEFAULT_API_URL, get_profiles, print_output, print_table
3
4
 
4
5
 
5
6
  # DEFINE THE PROFILE COMMAND GROUP
@@ -62,15 +63,18 @@ def add_profile(profile_name, access_key, secret_key, username, password, region
62
63
 
63
64
  @profile.command('update', help="Update an existing profile")
64
65
  @click.option('--profile-name', required=True, help="Name of profile", type=click.STRING)
66
+ @click.option('--new-name', required=False, help="Update profile name with new one, USE IT WITH CAUTION", type=click.STRING)
65
67
  @click.option('--region', required=False, help="Region name", type=click.Choice(['eu-west-2', 'cloudgouv-eu-west-1']))
66
68
  @click.option('--endpoint', required=False, help="API endpoint", type=click.STRING)
67
69
  @click.option('--jwt', required=False, help="Enable jwt, by default is false", type=click.BOOL)
68
- def update_profile(profile_name, region, endpoint, jwt):
70
+ @click.option('--force', is_flag=True, help="Force update profile name without confirmation")
71
+ def update_profile(profile_name, region, endpoint, jwt, new_name, force):
69
72
  """Update configuration settings for an existing profile."""
70
73
  profiles = profile_list()
71
74
  if profile_name not in profiles:
72
- raise click.ClickException(f"There no profile with name: {profile_name}")
75
+ raise click.ClickException(f"There is no profile with name: {profile_name}")
73
76
 
77
+ msg = f"Profile {profile_name} has been successfully updated"
74
78
  profile = profiles[profile_name]
75
79
  if region:
76
80
  profile["region_name"] = region
@@ -81,11 +85,17 @@ def update_profile(profile_name, region, endpoint, jwt):
81
85
  if jwt is not None:
82
86
  profile["jwt"] = jwt
83
87
 
84
- set_profile(profile_name, profile)
88
+ if new_name is not None:
89
+ old_profile = click.style(profile_name, bold=True)
90
+ new_profile = click.style(new_name, bold=True)
91
+ if force or click.confirm(f"Are you sure you want to update the profile {old_profile} with new name {new_profile}?", abort=True):
92
+ remove_profile(profile_name)
93
+ profile_name = new_name
94
+ msg = f"Profile {old_profile} has been successfully updated with new name {new_profile}"
85
95
 
96
+ set_profile(profile_name, profile)
86
97
  profile_name = click.style(profile_name, bold=True)
87
-
88
- click.echo(f"Profile {profile_name} has been successfully updated")
98
+ click.echo(msg)
89
99
 
90
100
  @profile.command('delete', help="Delete a profile by name")
91
101
  @click.option('--profile-name', required=True, help="Name of profile", type=click.STRING)
@@ -103,7 +113,8 @@ def delete_profile(profile_name, force):
103
113
  click.echo(f"Profile {profile_name_bold} has been successfully deleted")
104
114
 
105
115
  @profile.command('list', help="List existing profiles")
106
- def list_profiles():
116
+ @click.option('-o', '--output', type=click.Choice(["json", "yaml", "table", "wide"]), help="Specify output format, by default is wide")
117
+ def list_profiles(output):
107
118
  """Display all configured profiles with their settings."""
108
119
  profiles = profile_list()
109
120
 
@@ -111,18 +122,49 @@ def list_profiles():
111
122
  return click.echo("There are no profiles")
112
123
 
113
124
  profiles_keys = list(profiles.keys())
125
+ lines = list()
114
126
 
115
127
  for key in profiles_keys:
116
128
  if 'endpoint' not in profiles[key]:
117
129
  if 'region_name' in profiles[key]:
118
- endpoint = click.style(DEFAULT_API_URL.format(region=profiles[key]['region_name']), bold=True)
130
+ endpoint = DEFAULT_API_URL.format(region=profiles[key]['region_name'])
119
131
  else:
120
132
  endpoint = None
121
133
  else:
122
- endpoint = click.style(profiles[key]["endpoint"], bold=True)
123
- name = click.style(key, bold=True)
124
- account_type = click.style(profiles[key]["type"], bold=True)
125
- region = click.style(profiles[key]["region_name"], bold=True)
126
- jwt = click.style(profiles[key].get("jwt", False), bold=True)
127
-
128
- click.echo(f"Profile: {name} Account type: {account_type} Region: {region} Endpoint: {endpoint} Enabled JWT auth: {jwt}")
134
+ endpoint = profiles[key]["endpoint"]
135
+
136
+ name = key
137
+ account_type = profiles[key]["type"]
138
+ region = profiles[key]["region_name"]
139
+ jwt = profiles[key].get("jwt", False)
140
+ # Remove credentials keys from profiles
141
+ profiles[key].pop('access_key', None)
142
+ profiles[key].pop('secret_key', None)
143
+ profiles[key].pop('username', None)
144
+ profiles[key].pop('password', None)
145
+ # Add endpoint and JWT to dict
146
+ profiles[key].update({'endpoint': endpoint})
147
+ profiles[key].update({'jwt': jwt})
148
+
149
+ if output == 'wide' or output is None:
150
+ lines.append("Profile: {} Account type: {} Region: {} Endpoint: {} Enabled JWT auth: {}".format(
151
+ click.style(name, bold=True),
152
+ click.style(account_type, bold=True),
153
+ click.style(region, bold=True),
154
+ click.style(endpoint, bold=True),
155
+ click.style(jwt, bold=True)))
156
+ else:
157
+ lines.append({"name": name, "account_type": account_type, "region": region, "endpoint": endpoint, "jwt": jwt})
158
+
159
+ if output == "table":
160
+ print_table(lines, [["Profile", "name"],
161
+ ["Account type", "account_type"],
162
+ ["Region", "region"],
163
+ ["Endpoint", "endpoint"],
164
+ ["JWT enabled", "jwt"]])
165
+ elif output in ["json", "yaml"]:
166
+ print_output(profiles, output)
167
+ else:
168
+ for line in lines:
169
+ click.echo(line)
170
+ return
@@ -6,12 +6,12 @@ import human_readable
6
6
  import prettytable
7
7
  import os
8
8
 
9
- from .utils import do_request, print_output, find_project_id_by_name, get_project_id, set_project_id, detect_and_parse_input, transform_tuple, ctx_update, set_cluster_id, get_template, get_project_name, format_changed_row, is_interesting_status, login_profile, profile_completer
9
+ from .utils import do_request, print_output, print_table, find_project_id_by_name, get_project_id, set_project_id, detect_and_parse_input, transform_tuple, ctx_update, set_cluster_id, get_template, get_project_name, format_changed_row, is_interesting_status, login_profile, profile_completer, project_completer
10
10
 
11
11
  # DEIFNE THE PROJECT COMMAND GROUP
12
12
  @click.group(help="Project related commands.")
13
13
  @click.option('--project', 'project_name', required = False, help="Project Name")
14
- @click.option('--project-name', '-p', required = False, help="Project Name")
14
+ @click.option('--project-name', '-p', required = False, help="Project Name", shell_complete=project_completer)
15
15
  @click.option("--profile", help="Configuration profile to use", shell_complete=profile_completer)
16
16
  @click.pass_context
17
17
  def project(ctx, project_name, profile):
@@ -20,7 +20,7 @@ def project(ctx, project_name, profile):
20
20
 
21
21
  # LOGIN ON PROJECT
22
22
  @project.command('login', help="Set a default project by name")
23
- @click.option('--project-name', '-p', required=False, help="Name of project", type=click.STRING)
23
+ @click.option('--project-name', '-p', required=False, help="Name of project", type=click.STRING, shell_complete=project_completer)
24
24
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
25
25
  @click.pass_context
26
26
  def project_login(ctx, project_name, profile):
@@ -58,7 +58,7 @@ def project_logout(ctx, profile):
58
58
 
59
59
  # LIST PROJECTS
60
60
  @project.command('list', help="List all projects")
61
- @click.option('--project-name', '-p', help="Name of project", type=click.STRING)
61
+ @click.option('--project-name', '-p', help="Name of project", type=click.STRING, shell_complete=project_completer)
62
62
  @click.option('--deleted', is_flag=True, help="List deleted projects")
63
63
  @click.option('--plain', is_flag=True, help="Plain table format")
64
64
  @click.option('--msword', is_flag=True, help="Microsoft Word table format")
@@ -193,7 +193,7 @@ def project_list(ctx, project_name, deleted, plain, msword, uuid, watch, output,
193
193
 
194
194
  # CREATE PROJECT BY NAME
195
195
  @project.command('create', help="Create a new project")
196
- @click.option('--project-name', '-p', help="Name of the project")
196
+ @click.option('--project-name', '-p', help="Name of the project", shell_complete=project_completer)
197
197
  @click.option('--description', help="Description of the project")
198
198
  @click.option('--cidr', help='CIDR for the project')
199
199
  @click.option('--quirk', multiple=True, help="Quirk")
@@ -254,7 +254,7 @@ def project_create(ctx, project_name, description, cidr, quirk, tags, disable_ap
254
254
 
255
255
  # GET PROJECT BY NAME
256
256
  @project.command('get', help="Get default project or the project by name")
257
- @click.option('--project-name', '-p', help="Name of the project")
257
+ @click.option('--project-name', '-p', help="Name of the project", shell_complete=project_completer)
258
258
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
259
259
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
260
260
  @click.pass_context
@@ -270,7 +270,7 @@ def project_get(ctx, project_name, output, profile):
270
270
 
271
271
  # DELETE PROJECT BY NAME
272
272
  @project.command('delete', help="Delete a project by name")
273
- @click.option('--project-name', '-p', required=False, help="Project Name")
273
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
274
274
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
275
275
  @click.option('--dry-run', is_flag=True, help="Run without any action")
276
276
  @click.option('--force', is_flag=True, help="Force deletion without confirmation")
@@ -302,7 +302,7 @@ def project_delete_command(ctx, project_name, output, dry_run, force, profile):
302
302
 
303
303
  # UPDATE PROJECT BY NAME
304
304
  @project.command('update', help="Update a project by name")
305
- @click.option('--project-name', '-p', required=False, help="Project Name")
305
+ @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
306
306
  @click.option('--description', help="Description of the project")
307
307
  @click.option('--quirk', multiple=True, help="Quirk")
308
308
  @click.option('--tags', help="Comma-separated list of tags, example: 'key1=value1,key2=value2'")
@@ -350,8 +350,8 @@ def project_update_command(ctx, project_name, description, quirk, tags, disable_
350
350
 
351
351
  # GET PROJECT QUOTAS BY PROJECT NAME
352
352
  @project.command('quotas', help="Get project quotas")
353
- @click.option('--project-name', '-p', help="Name of the project")
354
- @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
353
+ @click.option('--project-name', '-p', help="Name of the project", shell_complete=project_completer)
354
+ @click.option('-o', '--output', type=click.Choice(["json", "yaml", "table"]), help="Specify output format, by default is json")
355
355
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
356
356
  @click.pass_context
357
357
  def project_get_quotas(ctx, project_name, output, profile):
@@ -362,12 +362,23 @@ def project_get_quotas(ctx, project_name, output, profile):
362
362
  project_id = find_project_id_by_name(project_name)
363
363
 
364
364
  data = do_request("GET", f'projects/{project_id}/quotas')["data"]
365
- print_output(data, output)
365
+ if output == "table":
366
+ print_table(data["quotas"], [["Name", "Name"],
367
+ ["Collection", "QuotaCollection"],
368
+ ["Description", "ShortDescription"],
369
+ ["Max Value", "MaxValue"],
370
+ ["Used Value", "UsedValue"]])
371
+ print_table(data["subregions"], [["Region", "RegionName"],
372
+ ["Availability Zone", "SubregionName"],
373
+ ["State", "State"]])
374
+
375
+ else:
376
+ print_output(data, output)
366
377
 
367
378
 
368
379
  # GET PROJECT SNAPSHOTS BY PROJECT NAME
369
380
  @project.command('snapshots', help="Get project snapshots")
370
- @click.option('--project-name', '-p', help="Name of the project")
381
+ @click.option('--project-name', '-p', help="Name of the project", shell_complete=project_completer)
371
382
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
372
383
  @click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
373
384
  @click.pass_context
@@ -383,7 +394,7 @@ def project_get(ctx, project_name, output, profile):
383
394
 
384
395
  # GET PUBLIC IPS BY PROJECT NAME
385
396
  @project.command('publicips', help="Get project public ips")
386
- @click.option('--project-name', '-p', help="Name of the project")
397
+ @click.option('--project-name', '-p', help="Name of the project", shell_complete=project_completer)
387
398
  @click.option('-o', '--output', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json")
388
399
  @click.option('--profile', help="Configuration profile to use")
389
400
  @click.pass_context
@@ -0,0 +1,21 @@
1
+ import click
2
+ import prettytable
3
+ from .utils import do_request, print_output, print_table, ctx_update, login_profile, profile_completer
4
+
5
+ @click.command(help="Get Quotas")
6
+ @click.option("--profile", help="Configuration profile to use", shell_complete=profile_completer)
7
+ @click.option('-o', '--output', type=click.Choice(["json", "yaml", "table"]), help="Specify output format, by default is json")
8
+ @click.pass_context
9
+ def quotas(ctx, profile, output):
10
+ """Retrieve global quotas across all projects for the given profile."""
11
+ _, _, profile = ctx_update(ctx, None, None, profile)
12
+ login_profile(profile)
13
+
14
+ data = do_request("GET", 'quotas')
15
+ if output == "table":
16
+ print_table([data], [["Projects", "Projects"],
17
+ ["Clusters per project", "ClustersPerProject"],
18
+ ["Kubernetes versions", "KubeVersions"],
19
+ ["Control plane subregions", "CPSubregions"]])
20
+ else:
21
+ print_output(data, output)