oks-cli 1.21__tar.gz → 1.22__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.
- {oks_cli-1.21 → oks_cli-1.22}/PKG-INFO +1 -1
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/cluster.py +4 -4
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/project.py +2 -1
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/user.py +35 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/utils.py +34 -3
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli.egg-info/PKG-INFO +1 -1
- {oks_cli-1.21 → oks_cli-1.22}/setup.py +1 -1
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_user.py +33 -1
- {oks_cli-1.21 → oks_cli-1.22}/LICENSE +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/README.md +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/__init__.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/cache.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/main.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/netpeering.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/profile.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli/quotas.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli.egg-info/SOURCES.txt +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli.egg-info/dependency_links.txt +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli.egg-info/entry_points.txt +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli.egg-info/requires.txt +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/oks_cli.egg-info/top_level.txt +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/setup.cfg +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_cache.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_cluster.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_netpeering.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_nodepool.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_profile.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_project.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_quota.py +0 -0
- {oks_cli-1.21 → oks_cli-1.22}/tests/test_shell_completion.py +0 -0
|
@@ -373,8 +373,8 @@ def _create_cluster(project_name, cluster_config, output):
|
|
|
373
373
|
@click.option('--cidr-service', help='CIDR of services')
|
|
374
374
|
@click.option('--control-plane', shell_complete=shell_completions, help="Controlplane plan")
|
|
375
375
|
@click.option('--zone', '-z', multiple=True, shell_complete=shell_completions, help="List of Control Plane availability zones")
|
|
376
|
-
@click.option('--enable-admission-plugins', help="List of admission plugins, separated by commas")
|
|
377
|
-
@click.option('--disable-admission-plugins', help="List of admission plugins, separated by commas")
|
|
376
|
+
@click.option('--enable-admission-plugins', shell_complete=shell_completions, help="List of admission plugins, separated by commas")
|
|
377
|
+
@click.option('--disable-admission-plugins', shell_complete=shell_completions, help="List of admission plugins, separated by commas")
|
|
378
378
|
@click.option('--quirk', '-q', multiple=True, help="Quirk")
|
|
379
379
|
@click.option('--tags', '-t', help="Comma-separated list of tags, example: 'key1=value1,key2=value2'")
|
|
380
380
|
@click.option('--disable-api-termination', type=click.BOOL, help="Disable delete action by API")
|
|
@@ -472,8 +472,8 @@ def cluster_create_command(ctx, project_name, cluster_name, description, admin,
|
|
|
472
472
|
@click.option('--admin', '-a', help="Admin Whitelist ips. you can use 'my-ip' to automatically use your current IP.")
|
|
473
473
|
@click.option('--version', '-v', shell_complete=shell_completions, help="Kubernetes version")
|
|
474
474
|
@click.option('--tags', '-t', help="Comma-separated list of tags, example: 'key1=value1,key2=value2'")
|
|
475
|
-
@click.option('--enable-admission-plugins', help="List of admission plugins, separated by commas")
|
|
476
|
-
@click.option('--disable-admission-plugins', help="List of admission plugins, separated by commas")
|
|
475
|
+
@click.option('--enable-admission-plugins', shell_complete=shell_completions, help="List of admission plugins, separated by commas")
|
|
476
|
+
@click.option('--disable-admission-plugins', shell_complete=shell_completions, help="List of admission plugins, separated by commas")
|
|
477
477
|
@click.option('--quirk', '-q', multiple=True, help="Quirk")
|
|
478
478
|
@click.option('--disable-api-termination', type=click.BOOL, help="Disable delete action by API")
|
|
479
479
|
@click.option('--control-plane', shell_complete=shell_completions, help="Controlplane plan")
|
|
@@ -365,7 +365,8 @@ def project_get_quotas(ctx, project_name, output, profile):
|
|
|
365
365
|
["Collection", "QuotaCollection"],
|
|
366
366
|
["Description", "ShortDescription"],
|
|
367
367
|
["Max Value", "MaxValue"],
|
|
368
|
-
["Used Value", "UsedValue"]
|
|
368
|
+
["Used Value", "UsedValue"],
|
|
369
|
+
["AccountId", "AccountId"]])
|
|
369
370
|
print_table(data["subregions"], [["Region", "RegionName"],
|
|
370
371
|
["Availability Zone", "SubregionName"],
|
|
371
372
|
["State", "State"]])
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
|
|
8
8
|
from nacl.public import PrivateKey, SealedBox
|
|
9
9
|
from nacl.encoding import Base64Encoder
|
|
10
|
+
from prettytable import TableStyle
|
|
10
11
|
|
|
11
12
|
from .utils import do_request, print_output, find_project_id_by_name, ctx_update, login_profile, profile_completer, project_completer, JSONClickException
|
|
12
13
|
|
|
@@ -166,4 +167,38 @@ def user_delete(ctx, project_name, user, output, dry_run, force, profile):
|
|
|
166
167
|
if force or click.confirm(f"Are you sure you want to delete the user '{user}'?", abort=True):
|
|
167
168
|
data = do_request("DELETE", f"projects/{project_id}/eim_users/{user}")
|
|
168
169
|
print_output(data, output)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@user.command('types', help="List available user types")
|
|
173
|
+
@click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer)
|
|
174
|
+
@click.option('--output', '-o', type=click.Choice(["json", "yaml"]), help="Specify output format")
|
|
175
|
+
@click.option('--plain', is_flag=True, help="Plain table format")
|
|
176
|
+
@click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer)
|
|
177
|
+
@click.pass_context
|
|
178
|
+
def user_types(ctx, project_name, output, plain, profile):
|
|
179
|
+
"""Display available user types."""
|
|
180
|
+
project_name, _, profile = ctx_update(ctx, project_name, None, profile)
|
|
181
|
+
login_profile(profile)
|
|
182
|
+
|
|
183
|
+
project_id = find_project_id_by_name(project_name)
|
|
184
|
+
|
|
185
|
+
data = do_request("GET", f'projects/{project_id}/eim_users/types')
|
|
186
|
+
|
|
187
|
+
if output:
|
|
188
|
+
print_output(data, output)
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
table = prettytable.PrettyTable()
|
|
192
|
+
table.field_names = ["USER TYPE", "DESCRIPTION"]
|
|
193
|
+
|
|
194
|
+
if plain:
|
|
195
|
+
table.set_style(TableStyle.PLAIN_COLUMNS)
|
|
196
|
+
|
|
197
|
+
for entry in data:
|
|
198
|
+
table.add_row([
|
|
199
|
+
entry.get("UserType", ""),
|
|
200
|
+
entry.get("Description") or "-"
|
|
201
|
+
])
|
|
202
|
+
|
|
203
|
+
click.echo(table)
|
|
169
204
|
|
|
@@ -83,8 +83,12 @@ def find_response_object(data):
|
|
|
83
83
|
return response["EimUsers"]
|
|
84
84
|
elif key == "EimUser":
|
|
85
85
|
return response["EimUser"]
|
|
86
|
+
elif key == "AdmissionPlugins":
|
|
87
|
+
return response["AdmissionPlugins"]
|
|
86
88
|
elif key == "Data":
|
|
87
89
|
return response
|
|
90
|
+
elif key == "EimUserTypes":
|
|
91
|
+
return response["EimUserTypes"]
|
|
88
92
|
|
|
89
93
|
raise click.ClickException("The API response format is incorrect.")
|
|
90
94
|
|
|
@@ -847,9 +851,11 @@ def kubeconfig_parse_fields(kubeconfig, cluster_name, user, group):
|
|
|
847
851
|
|
|
848
852
|
return kubedata
|
|
849
853
|
|
|
850
|
-
def retrieve_cp_sized(filepath, endpoint):
|
|
854
|
+
def retrieve_cp_sized(filepath, endpoint, key = None):
|
|
851
855
|
"""Fetch control plane sizes from API and save to file."""
|
|
852
856
|
cp_list = do_request("GET", endpoint)
|
|
857
|
+
if key:
|
|
858
|
+
cp_list = cp_list.get(key)
|
|
853
859
|
|
|
854
860
|
with open(filepath, "w") as file:
|
|
855
861
|
json.dump(cp_list, file)
|
|
@@ -871,6 +877,8 @@ def shell_completions(ctx, param: click.core.Option, incomplete):
|
|
|
871
877
|
if profile not in profiles:
|
|
872
878
|
return []
|
|
873
879
|
|
|
880
|
+
key = None
|
|
881
|
+
|
|
874
882
|
login_profile(profile)
|
|
875
883
|
|
|
876
884
|
if param.name == "version":
|
|
@@ -879,6 +887,16 @@ def shell_completions(ctx, param: click.core.Option, incomplete):
|
|
|
879
887
|
endpoint = "clusters/limits/control_plane_plans"
|
|
880
888
|
elif param.name == "zone":
|
|
881
889
|
endpoint = "clusters/limits/cp_subregions"
|
|
890
|
+
elif param.name == "disable_admission_plugins" and ctx.params["version"]:
|
|
891
|
+
key = "DisableAdmissionPlugins"
|
|
892
|
+
version = ctx.params["version"]
|
|
893
|
+
endpoint = f"clusters/limits/admission_plugins?version={version}"
|
|
894
|
+
param.name += f".{version}"
|
|
895
|
+
elif param.name == "enable_admission_plugins" and ctx.params["version"]:
|
|
896
|
+
key = "EnableAdmissionPlugins"
|
|
897
|
+
version = ctx.params["version"]
|
|
898
|
+
endpoint = f"clusters/limits/admission_plugins?version={version}"
|
|
899
|
+
param.name += f".{version}"
|
|
882
900
|
else:
|
|
883
901
|
return []
|
|
884
902
|
|
|
@@ -888,9 +906,9 @@ def shell_completions(ctx, param: click.core.Option, incomplete):
|
|
|
888
906
|
if os.path.exists(CP_SIZES_PATH):
|
|
889
907
|
file_ctime = os.path.getctime(CP_SIZES_PATH)
|
|
890
908
|
if datetime.timestamp(datetime.now()) - file_ctime > 300:
|
|
891
|
-
retrieve_cp_sized(CP_SIZES_PATH, endpoint)
|
|
909
|
+
retrieve_cp_sized(CP_SIZES_PATH, endpoint, key)
|
|
892
910
|
else:
|
|
893
|
-
retrieve_cp_sized(CP_SIZES_PATH, endpoint)
|
|
911
|
+
retrieve_cp_sized(CP_SIZES_PATH, endpoint, key)
|
|
894
912
|
|
|
895
913
|
if os.path.exists(CP_SIZES_PATH):
|
|
896
914
|
with open(CP_SIZES_PATH, "r") as file:
|
|
@@ -898,6 +916,19 @@ def shell_completions(ctx, param: click.core.Option, incomplete):
|
|
|
898
916
|
else:
|
|
899
917
|
cp_list = []
|
|
900
918
|
|
|
919
|
+
# Handle comma-separated values
|
|
920
|
+
if "," in incomplete:
|
|
921
|
+
parts = incomplete.split(",")
|
|
922
|
+
selected = set(parts[:-1])
|
|
923
|
+
current = parts[-1]
|
|
924
|
+
prefix = ",".join(parts[:-1])
|
|
925
|
+
|
|
926
|
+
return [
|
|
927
|
+
f"{prefix},{item}"
|
|
928
|
+
for item in cp_list
|
|
929
|
+
if item.startswith(current) and item not in selected
|
|
930
|
+
]
|
|
931
|
+
|
|
901
932
|
return [k for k in cp_list if k.startswith(incomplete)]
|
|
902
933
|
|
|
903
934
|
def update_shell_profile(shell_profile, filepath):
|
|
@@ -53,4 +53,36 @@ def test_user_delete_command(mock_request, add_default_profile):
|
|
|
53
53
|
runner = CliRunner()
|
|
54
54
|
result = runner.invoke(cli, ["user", "delete", "-p", "test", "-u", "OKSAuditor", "--force"])
|
|
55
55
|
assert result.exit_code == 0
|
|
56
|
-
assert 'User has been deleted.' in result.output
|
|
56
|
+
assert 'User has been deleted.' in result.output
|
|
57
|
+
|
|
58
|
+
@patch("oks_cli.utils.requests.request")
|
|
59
|
+
def test_user_types_command(mock_request, add_default_profile):
|
|
60
|
+
mock_request.side_effect = [
|
|
61
|
+
MagicMock(status_code=200, headers={}, json=lambda: {"ResponseContext": {}, "Projects": [{"id": "12345"}]}),
|
|
62
|
+
MagicMock(status_code=200, headers={}, json=lambda: {"ResponseContext": {}, "EimUserTypes": [
|
|
63
|
+
{"UserType": "OKSSnapshotsManager", "Description": "OKS user with full access to snapshots"},
|
|
64
|
+
{"UserType": "OKSVolumesManager", "Description": "OKS user with management access to volumes BSU"},
|
|
65
|
+
]})
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
runner = CliRunner()
|
|
69
|
+
result = runner.invoke(cli, ["user", "types", "-p", "test"])
|
|
70
|
+
assert result.exit_code == 0
|
|
71
|
+
assert 'OKSSnapshotsManager' in result.output
|
|
72
|
+
assert 'OKSVolumesManager' in result.output
|
|
73
|
+
|
|
74
|
+
@patch("oks_cli.utils.requests.request")
|
|
75
|
+
def test_user_types_command_json(mock_request, add_default_profile):
|
|
76
|
+
mock_request.side_effect = [
|
|
77
|
+
MagicMock(status_code=200, headers={}, json=lambda: {"ResponseContext": {}, "Projects": [{"id": "12345"}]}),
|
|
78
|
+
MagicMock(status_code=200, headers={}, json=lambda: {"ResponseContext": {}, "EimUserTypes": [
|
|
79
|
+
{"UserType": "OKSSnapshotsManager", "Description": "OKS user with full access to snapshots"},
|
|
80
|
+
{"UserType": "OKSVolumesManager", "Description": "OKS user with management access to volumes BSU"},
|
|
81
|
+
]})
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
runner = CliRunner()
|
|
85
|
+
result = runner.invoke(cli, ["user", "types", "-p", "test", "-o", "json"])
|
|
86
|
+
assert result.exit_code == 0
|
|
87
|
+
assert 'OKSSnapshotsManager' in result.output
|
|
88
|
+
assert 'OKS user with full access to snapshots' in result.output
|
|
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
|
|
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
|