kaqing 2.0.14__py3-none-any.whl → 2.0.189__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kaqing might be problematic. Click here for more details.
- adam/__init__.py +0 -2
- adam/app_session.py +9 -12
- adam/apps.py +20 -6
- adam/batch.py +16 -6
- adam/checks/check_utils.py +19 -49
- adam/checks/compactionstats.py +1 -1
- adam/checks/cpu.py +9 -3
- adam/checks/cpu_metrics.py +52 -0
- adam/checks/disk.py +3 -4
- adam/checks/gossip.py +1 -1
- adam/checks/memory.py +3 -3
- adam/checks/status.py +1 -1
- adam/columns/columns.py +3 -1
- adam/columns/cpu.py +3 -1
- adam/columns/cpu_metrics.py +22 -0
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +24 -0
- adam/commands/alter_tables.py +66 -0
- adam/commands/app/app.py +38 -0
- adam/commands/{app_ping.py → app/app_ping.py} +8 -14
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/{show → app}/show_app_id.py +9 -12
- adam/commands/{show → app}/show_app_queues.py +8 -14
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/__init__.py +0 -0
- adam/commands/audit/audit.py +67 -0
- adam/commands/audit/audit_repair_tables.py +72 -0
- adam/commands/audit/audit_run.py +50 -0
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +36 -0
- adam/commands/audit/show_slow10.py +36 -0
- adam/commands/audit/show_top10.py +36 -0
- adam/commands/audit/utils_show_top10.py +71 -0
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +36 -0
- adam/commands/bash/bash_completer.py +93 -0
- adam/commands/bash/utils_bash.py +16 -0
- adam/commands/cat.py +36 -0
- adam/commands/cd.py +14 -88
- adam/commands/check.py +18 -21
- adam/commands/cli_commands.py +11 -7
- adam/commands/clipboard_copy.py +87 -0
- adam/commands/code.py +57 -0
- adam/commands/command.py +220 -19
- adam/commands/commands_utils.py +28 -31
- adam/commands/cql/__init__.py +0 -0
- adam/commands/cql/completions_c.py +28 -0
- adam/commands/{cqlsh.py → cql/cqlsh.py} +13 -32
- adam/commands/cql/utils_cql.py +305 -0
- adam/commands/deploy/code_start.py +7 -10
- adam/commands/deploy/code_stop.py +4 -21
- adam/commands/deploy/code_utils.py +5 -5
- adam/commands/deploy/deploy.py +4 -40
- adam/commands/deploy/deploy_frontend.py +15 -18
- adam/commands/deploy/deploy_pg_agent.py +4 -7
- adam/commands/deploy/deploy_pod.py +74 -77
- adam/commands/deploy/deploy_utils.py +16 -26
- adam/commands/deploy/undeploy.py +4 -40
- adam/commands/deploy/undeploy_frontend.py +5 -8
- adam/commands/deploy/undeploy_pg_agent.py +7 -8
- adam/commands/deploy/undeploy_pod.py +16 -17
- adam/commands/devices/__init__.py +0 -0
- adam/commands/devices/device.py +149 -0
- adam/commands/devices/device_app.py +163 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +179 -0
- adam/commands/devices/device_export.py +87 -0
- adam/commands/devices/device_postgres.py +160 -0
- adam/commands/devices/devices.py +25 -0
- adam/commands/download_file.py +47 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/__init__.py +0 -0
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +39 -0
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +39 -0
- adam/commands/export/drop_export_databases.py +37 -0
- adam/commands/export/export.py +37 -0
- adam/commands/export/export_databases.py +246 -0
- adam/commands/export/export_select.py +34 -0
- adam/commands/export/export_sessions.py +209 -0
- adam/commands/export/export_use.py +49 -0
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +332 -0
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +44 -0
- adam/commands/export/importer.py +81 -0
- adam/commands/export/importer_athena.py +148 -0
- adam/commands/export/importer_sqlite.py +67 -0
- adam/commands/export/show_column_counts.py +45 -0
- adam/commands/export/show_export_databases.py +39 -0
- adam/commands/export/show_export_session.py +39 -0
- adam/commands/export/show_export_sessions.py +37 -0
- adam/commands/export/utils_export.py +344 -0
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +14 -9
- adam/commands/intermediate_command.py +52 -0
- adam/commands/issues.py +14 -40
- adam/commands/kubectl.py +38 -0
- adam/commands/login.py +26 -25
- adam/commands/logs.py +5 -7
- adam/commands/ls.py +11 -115
- adam/commands/medusa/medusa.py +4 -46
- adam/commands/medusa/medusa_backup.py +22 -29
- adam/commands/medusa/medusa_restore.py +51 -49
- adam/commands/medusa/medusa_show_backupjobs.py +20 -21
- adam/commands/medusa/medusa_show_restorejobs.py +16 -21
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +8 -17
- adam/commands/param_get.py +11 -14
- adam/commands/param_set.py +9 -13
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +49 -73
- adam/commands/postgres/postgres_databases.py +270 -0
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/utils_postgres.py +79 -0
- adam/commands/preview_table.py +10 -69
- adam/commands/pwd.py +14 -43
- adam/commands/reaper/reaper.py +6 -49
- adam/commands/reaper/reaper_forward.py +49 -56
- adam/commands/reaper/reaper_forward_session.py +6 -0
- adam/commands/reaper/reaper_forward_stop.py +10 -16
- adam/commands/reaper/reaper_restart.py +8 -15
- adam/commands/reaper/reaper_run_abort.py +8 -33
- adam/commands/reaper/reaper_runs.py +43 -58
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +14 -33
- adam/commands/reaper/reaper_schedule_start.py +9 -33
- adam/commands/reaper/reaper_schedule_stop.py +9 -33
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +203 -0
- adam/commands/repair/repair.py +4 -46
- adam/commands/repair/repair_log.py +6 -12
- adam/commands/repair/repair_run.py +29 -36
- adam/commands/repair/repair_scan.py +33 -41
- adam/commands/repair/repair_stop.py +6 -13
- adam/commands/report.py +25 -21
- adam/commands/restart.py +27 -28
- adam/commands/rollout.py +20 -25
- adam/commands/shell.py +12 -4
- adam/commands/show/show.py +15 -46
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +37 -0
- adam/commands/show/show_cassandra_status.py +48 -52
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_cli_commands.py +56 -0
- adam/commands/show/show_host.py +33 -0
- adam/commands/show/show_login.py +23 -27
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +18 -21
- adam/commands/show/show_storage.py +11 -20
- adam/commands/watch.py +27 -30
- adam/config.py +8 -6
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/pod_exec_result.py +13 -5
- adam/repl.py +136 -120
- adam/repl_commands.py +66 -24
- adam/repl_session.py +8 -1
- adam/repl_state.py +343 -73
- adam/sql/__init__.py +0 -0
- adam/sql/lark_completer.py +284 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/sql_completer.py +118 -0
- adam/sql/sql_state_machine.py +630 -0
- adam/sql/term_completer.py +76 -0
- adam/sso/authn_ad.py +7 -9
- adam/sso/authn_okta.py +4 -6
- adam/sso/cred_cache.py +4 -6
- adam/sso/idp.py +10 -13
- adam/utils.py +539 -11
- adam/utils_athena.py +145 -0
- adam/utils_audits.py +102 -0
- adam/utils_issues.py +32 -0
- adam/utils_k8s/__init__.py +0 -0
- adam/utils_k8s/app_clusters.py +28 -0
- adam/utils_k8s/app_pods.py +36 -0
- adam/utils_k8s/cassandra_clusters.py +44 -0
- adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
- adam/{k8s_utils → utils_k8s}/custom_resources.py +16 -17
- adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
- adam/{k8s_utils → utils_k8s}/ingresses.py +2 -2
- adam/{k8s_utils → utils_k8s}/jobs.py +7 -11
- adam/utils_k8s/k8s.py +96 -0
- adam/{k8s_utils → utils_k8s}/kube_context.py +3 -3
- adam/{k8s_utils → utils_k8s}/pods.py +132 -83
- adam/{k8s_utils → utils_k8s}/secrets.py +7 -3
- adam/{k8s_utils → utils_k8s}/service_accounts.py +5 -4
- adam/{k8s_utils → utils_k8s}/services.py +2 -2
- adam/{k8s_utils → utils_k8s}/statefulsets.py +9 -16
- adam/utils_local.py +4 -0
- adam/utils_net.py +24 -0
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +172 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +137 -0
- adam/version.py +1 -1
- {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
- kaqing-2.0.189.dist-info/RECORD +253 -0
- kaqing-2.0.189.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- adam/commands/app.py +0 -67
- adam/commands/bash.py +0 -87
- adam/commands/cp.py +0 -95
- adam/commands/cql_utils.py +0 -53
- adam/commands/devices.py +0 -89
- adam/commands/postgres/postgres_session.py +0 -247
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/show/show_app_actions.py +0 -53
- adam/commands/show/show_commands.py +0 -61
- adam/commands/show/show_repairs.py +0 -47
- adam/k8s_utils/cassandra_clusters.py +0 -48
- kaqing-2.0.14.dist-info/RECORD +0 -167
- kaqing-2.0.14.dist-info/top_level.txt +0 -1
- /adam/{k8s_utils → commands/app}/__init__.py +0 -0
- /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
- /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
- {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
- {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
from adam.commands import extract_options, extract_sequence, extract_trailing_options
|
|
1
2
|
from adam.commands.command import Command
|
|
2
|
-
from adam.commands.
|
|
3
|
+
from adam.commands.cql.utils_cql import cassandra
|
|
3
4
|
from adam.config import Config
|
|
4
|
-
from adam.k8s_utils.statefulsets import StatefulSets
|
|
5
5
|
from adam.repl_state import ReplState, RequiredState
|
|
6
6
|
|
|
7
7
|
class ShowProcesses(Command):
|
|
@@ -26,28 +26,25 @@ class ShowProcesses(Command):
|
|
|
26
26
|
if not(args := self.args(cmd)):
|
|
27
27
|
return super().run(cmd, state)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
with self.validate(args, state) as (args, state):
|
|
30
|
+
with extract_trailing_options(args, '&') as (args, backgrounded):
|
|
31
|
+
with extract_options(args, ['-s', '--show']) as (args, show_out):
|
|
32
|
+
with extract_sequence(args, ['with', 'recipe', '=', 'qing']) as (_, recipe_qing):
|
|
33
|
+
cols = Config().get('processes.columns', 'pod,cpu-metrics,mem')
|
|
34
|
+
header = Config().get('processes.header', 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT')
|
|
35
|
+
if recipe_qing:
|
|
36
|
+
cols = Config().get('processes-qing.columns', 'pod,cpu,mem')
|
|
37
|
+
header = Config().get('processes-qing.header', 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT')
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
with cassandra(state) as pods:
|
|
40
|
+
pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded)
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
header = Config().get('processes.header', 'POD_NAME,CPU,MEM/LIMIT')
|
|
37
|
-
|
|
38
|
-
if state.pod:
|
|
39
|
-
show_table(state, [state.pod], cols, header, show_output=show_output)
|
|
40
|
-
elif state.sts:
|
|
41
|
-
pod_names = [pod.metadata.name for pod in StatefulSets.pods(state.sts, state.namespace)]
|
|
42
|
-
show_table(state, pod_names, cols, header, show_output=show_output)
|
|
43
|
-
|
|
44
|
-
return state
|
|
42
|
+
return state
|
|
45
43
|
|
|
46
44
|
def completion(self, state: ReplState):
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return super().completion(state)
|
|
45
|
+
recipes = ['metrics', 'qing']
|
|
46
|
+
return super().completion(state, {'with': {'recipe': {'=': {r: {'-s': {'&': None}, '&': None} for r in recipes}}}, '-s': {'&': None}, '&': None})
|
|
47
|
+
# return super().completion(state, {'with': {'recipe': {'=': {'metrics': {'-s': {'&': None}, '&': None}, 'qing': {'-s': {'&': None}}}}}, '-s': {'&': None}, '&': None})
|
|
51
48
|
|
|
52
49
|
def help(self, _: ReplState):
|
|
53
|
-
return f'{ShowProcesses.COMMAND} [-s]\t show process overview -s show commands on nodes'
|
|
50
|
+
return f'{ShowProcesses.COMMAND} [with recipe qing|metrics] [-s]\t show process overview -s show commands on nodes'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
from adam.commands import extract_options, extract_trailing_options
|
|
1
2
|
from adam.commands.command import Command
|
|
2
|
-
from adam.commands.
|
|
3
|
+
from adam.commands.cql.utils_cql import cassandra
|
|
3
4
|
from adam.config import Config
|
|
4
|
-
from adam.k8s_utils.statefulsets import StatefulSets
|
|
5
5
|
from adam.repl_state import ReplState, RequiredState
|
|
6
6
|
|
|
7
7
|
class ShowStorage(Command):
|
|
@@ -26,27 +26,18 @@ class ShowStorage(Command):
|
|
|
26
26
|
if not(args := self.args(cmd)):
|
|
27
27
|
return super().run(cmd, state)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
with self.validate(args, state) as (args, state):
|
|
30
|
+
with extract_trailing_options(args, '&') as (args, backgrounded):
|
|
31
|
+
with extract_options(args, ['-s', '--show']) as (args, show_out):
|
|
32
|
+
cols = Config().get('storage.columns', 'pod,volume_root,volume_cassandra,snapshots,data,compactions')
|
|
33
|
+
header = Config().get('storage.header', 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS')
|
|
34
|
+
with cassandra(state) as pods:
|
|
35
|
+
pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded)
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
cols = Config().get('storage.columns', 'pod,volume_root,volume_cassandra,snapshots,data,compactions')
|
|
36
|
-
header = Config().get('storage.header', 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS')
|
|
37
|
-
if state.pod:
|
|
38
|
-
show_table(state, [state.pod], cols, header, show_output=show_output)
|
|
39
|
-
elif state.sts:
|
|
40
|
-
pod_names = [pod.metadata.name for pod in StatefulSets.pods(state.sts, state.namespace)]
|
|
41
|
-
show_table(state, pod_names, cols, header, show_output=show_output)
|
|
42
|
-
|
|
43
|
-
return state
|
|
37
|
+
return state
|
|
44
38
|
|
|
45
39
|
def completion(self, state: ReplState):
|
|
46
|
-
|
|
47
|
-
return {}
|
|
48
|
-
|
|
49
|
-
return super().completion(state)
|
|
40
|
+
return super().completion(state, {'-s': {'&': None}, '&': None})
|
|
50
41
|
|
|
51
42
|
def help(self, _: ReplState):
|
|
52
43
|
return f'{ShowStorage.COMMAND} [-s]\t show storage overview -s show commands on nodes'
|
adam/commands/watch.py
CHANGED
|
@@ -6,12 +6,12 @@ from typing import List
|
|
|
6
6
|
from adam.commands.command import Command
|
|
7
7
|
from adam.commands.commands_utils import show_pods, show_rollout
|
|
8
8
|
from adam.config import Config
|
|
9
|
-
from adam.
|
|
9
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
10
10
|
from adam.repl_state import ReplState, RequiredState
|
|
11
|
-
from adam.utils import
|
|
11
|
+
from adam.utils import log2
|
|
12
12
|
|
|
13
13
|
class Watch(Command):
|
|
14
|
-
COMMAND = 'watch'
|
|
14
|
+
COMMAND = 'watch cassandra pods'
|
|
15
15
|
|
|
16
16
|
# the singleton pattern
|
|
17
17
|
def __new__(cls, *args, **kwargs):
|
|
@@ -26,37 +26,34 @@ class Watch(Command):
|
|
|
26
26
|
return Watch.COMMAND
|
|
27
27
|
|
|
28
28
|
def required(self):
|
|
29
|
-
return RequiredState.
|
|
29
|
+
return RequiredState.NAMESPACE
|
|
30
30
|
|
|
31
31
|
def run(self, cmd: str, state: ReplState):
|
|
32
32
|
if not(args := self.args(cmd)):
|
|
33
33
|
return super().run(cmd, state)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if not pods:
|
|
41
|
-
log2("No pods are found.")
|
|
42
|
-
return state
|
|
35
|
+
with self.validate(args, state) as (args, state):
|
|
36
|
+
pods = StatefulSets.pods(state.sts, state.namespace)
|
|
37
|
+
if not pods:
|
|
38
|
+
log2("No pods are found.")
|
|
39
|
+
return state
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
stop_event = threading.Event()
|
|
42
|
+
thread = threading.Thread(target=self.loop, args=(stop_event, state.sts, pods, state.namespace), daemon=True)
|
|
43
|
+
thread.start()
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
try:
|
|
46
|
+
log2(f"Press Ctrl+C to break.")
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
time.sleep(Config().get('watch.timeout', 3600 * 1))
|
|
49
|
+
except KeyboardInterrupt:
|
|
50
|
+
pass
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
log2("Stopping watch...")
|
|
53
|
+
stop_event.set()
|
|
54
|
+
thread.join()
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
return state
|
|
60
57
|
|
|
61
58
|
def loop(self, stop_flag: threading.Event, sts: str, pods: List[client.V1Pod], ns: str):
|
|
62
59
|
show_pods(pods, ns)
|
|
@@ -73,13 +70,13 @@ class Watch(Command):
|
|
|
73
70
|
cnt = Config().get('watch.interval', 10)
|
|
74
71
|
|
|
75
72
|
def completion(self, state: ReplState):
|
|
76
|
-
if state
|
|
77
|
-
|
|
73
|
+
if sc := super().completion(state):
|
|
74
|
+
if state.sts:
|
|
75
|
+
return sc
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
return {Watch.COMMAND: {n: None for n in StatefulSets.list_sts_names()}}
|
|
77
|
+
return super().completion(state, {n: None for n in StatefulSets.list_sts_names()})
|
|
81
78
|
|
|
82
|
-
return {
|
|
79
|
+
return {}
|
|
83
80
|
|
|
84
81
|
def help(self, _: ReplState):
|
|
85
|
-
return f'{Watch.COMMAND}\t watch pod changes'
|
|
82
|
+
return f'{Watch.COMMAND}\t watch Cassandra pod changes'
|
adam/config.py
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from typing import TypeVar, cast
|
|
2
3
|
import yaml
|
|
3
4
|
|
|
4
5
|
from . import __version__
|
|
5
|
-
from adam.utils import copy_config_file, get_deep_keys, log2
|
|
6
|
+
from adam.utils import LogConfig, copy_config_file, get_deep_keys, log2
|
|
6
7
|
|
|
7
8
|
T = TypeVar('T')
|
|
8
9
|
|
|
9
10
|
class Config:
|
|
10
11
|
EMBEDDED_PARAMS = {}
|
|
11
12
|
|
|
13
|
+
LogConfig.is_debug = lambda: Config().is_debug()
|
|
14
|
+
LogConfig.is_debug_complete = lambda: Config().get('debugs.complete', False)
|
|
15
|
+
LogConfig.is_debug_timing = lambda: Config().get('debugs.timings', False)
|
|
16
|
+
|
|
12
17
|
# the singleton pattern
|
|
13
18
|
def __new__(cls, *args, **kwargs):
|
|
14
19
|
if not hasattr(cls, 'instance'): cls.instance = super(Config, cls).__new__(cls)
|
|
@@ -17,6 +22,7 @@ class Config:
|
|
|
17
22
|
|
|
18
23
|
def __init__(self, path: str = None, is_user_entry = False):
|
|
19
24
|
if path:
|
|
25
|
+
self.wait_log_flag = False
|
|
20
26
|
try:
|
|
21
27
|
with open(path) as f:
|
|
22
28
|
self.params = cast(dict[str, any], yaml.safe_load(f))
|
|
@@ -37,11 +43,7 @@ class Config:
|
|
|
37
43
|
return get_deep_keys(self.params)
|
|
38
44
|
|
|
39
45
|
def is_debug(self):
|
|
40
|
-
return Config().get('debug
|
|
41
|
-
|
|
42
|
-
def debug(self, s: None):
|
|
43
|
-
if self.is_debug():
|
|
44
|
-
log2(f'DEBUG {s}')
|
|
46
|
+
return os.getenv('QING_DEV', 'false').lower() == 'true' or Config().get('debug', False)
|
|
45
47
|
|
|
46
48
|
def get(self, key: str, default: T) -> T:
|
|
47
49
|
# params['nodetool']['status']['max-nodes']
|
adam/embedded_params.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
def config():
|
|
2
|
-
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'logs': {'path': '/c3/cassandra/logs/system.log'}, '
|
|
2
|
+
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}, 'queries': {'last10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY ts DESC LIMIT {limit}", 'slow10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY CAST(duration AS REAL) DESC LIMIT {limit}", 'top10': "SELECT min(c) AS cluster, line, COUNT(*) AS cnt, avg(CAST(duration AS REAL)) AS duration\nFROM audit WHERE drive <> 'z' and ({date_condition})\nGROUP BY line ORDER BY cnt DESC LIMIT {limit}"}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'download': {'workers': 8}, 'export': {'workers': 8, 'csv_dir': '/c3/cassandra/tmp', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10', 'default-importer': 'sqlite', 'sqlite': {'workers': 8, 'columns': '<row-key>', 'local-db-dir': '/tmp/qing-db'}, 'athena': {'workers': 8, 'columns': '<keys>', 'bucket': 'c3.ops--qing'}, 'csv': {'workers': 8, 'columns': '<row-key>'}, 'log-prefix': '/tmp/qing'}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'local-tmp-dir': '/tmp/qing-db', 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-prefix': '/tmp/qing', 'nodetool': {'workers': 96, 'commands_in_line': 40, 'status': {'workers': 32, 'samples': 3, 'commands_in_line': 40}}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu-metrics,mem', 'header': 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT'}, 'processes-qing': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.14', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'c', 'a': {'auto-enter': 'c3/c3'}, 'c': {'auto-enter': 'cluster'}, 'x': {'auto-enter': 'latest'}, 'history': {'push-cat-log-file': True, 'push-cat-remote-log-file': True}, 'background-process': {'auto-nohup': True}}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'auto-complete': {'c': {'tables': 'lazy'}, 'x': {'tables': 'lazy'}, 'cli': {'cp': 'jit'}, 'export': {'databases': 'jit'}, 'medusa': {'backups': 'jit'}, 'reaper': {'schedules': 'lazy'}}, 'debug': False, 'debugs': {'timings': False, 'exit-on-error': False, 'show-parallelism': False}}
|
adam/log.py
CHANGED
|
@@ -4,6 +4,8 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
|
+
from adam.utils import log_exc
|
|
8
|
+
|
|
7
9
|
class Log:
|
|
8
10
|
DEBUG = False
|
|
9
11
|
|
|
@@ -28,7 +30,7 @@ class Log:
|
|
|
28
30
|
print(file=sys.stderr)
|
|
29
31
|
|
|
30
32
|
def log_to_file(config: dict[any, any]):
|
|
31
|
-
|
|
33
|
+
with log_exc():
|
|
32
34
|
base = f"/tmp/logs"
|
|
33
35
|
os.makedirs(base, exist_ok=True)
|
|
34
36
|
|
|
@@ -42,6 +44,4 @@ class Log:
|
|
|
42
44
|
except:
|
|
43
45
|
f.write(config)
|
|
44
46
|
else:
|
|
45
|
-
f.write(config)
|
|
46
|
-
except:
|
|
47
|
-
pass
|
|
47
|
+
f.write(config)
|
adam/pod_exec_result.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import yaml
|
|
2
2
|
|
|
3
|
+
from adam.utils import log_exc
|
|
4
|
+
|
|
3
5
|
class PodExecResult:
|
|
4
6
|
# {
|
|
5
7
|
# 'metadata': {},
|
|
@@ -15,19 +17,25 @@ class PodExecResult:
|
|
|
15
17
|
# ]
|
|
16
18
|
# }
|
|
17
19
|
# }
|
|
18
|
-
def __init__(self, stdout: str, stderr: str, command: str = None, error_output: str = None):
|
|
20
|
+
def __init__(self, stdout: str, stderr: str, command: str = None, error_output: str = None, pod: str = None, log_file: str = None):
|
|
19
21
|
self.stdout: str = stdout
|
|
20
22
|
self.stderr: str = stderr
|
|
21
23
|
self.command: str = command
|
|
22
24
|
if error_output:
|
|
23
25
|
self.error = yaml.safe_load(error_output)
|
|
26
|
+
self.pod = pod
|
|
27
|
+
self.log_file = log_file
|
|
24
28
|
|
|
25
29
|
def exit_code(self) -> int:
|
|
26
30
|
code = 0
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
with log_exc(False):
|
|
29
33
|
code = self.error['details']['causes'][0]['message']
|
|
30
|
-
except:
|
|
31
|
-
pass
|
|
32
34
|
|
|
33
|
-
return code
|
|
35
|
+
return code
|
|
36
|
+
|
|
37
|
+
def __str__(self):
|
|
38
|
+
return f'{"OK" if self.exit_code() == 0 else self.exit_code()} {self.command}'
|
|
39
|
+
|
|
40
|
+
def __audit_extra__(self):
|
|
41
|
+
return self.log_file if self.log_file else None
|
adam/repl.py
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import re
|
|
3
2
|
import time
|
|
4
|
-
import
|
|
3
|
+
from typing import cast
|
|
5
4
|
import click
|
|
6
|
-
from prompt_toolkit.completion import NestedCompleter
|
|
7
5
|
from prompt_toolkit.key_binding import KeyBindings
|
|
8
6
|
|
|
9
7
|
from adam.cli_group import cli
|
|
10
|
-
from adam.commands.command import Command
|
|
8
|
+
from adam.commands.command import Command, InvalidArgumentsException, InvalidStateException
|
|
11
9
|
from adam.commands.command_helpers import ClusterCommandHelper
|
|
10
|
+
from adam.commands.devices.devices import Devices
|
|
12
11
|
from adam.commands.help import Help
|
|
13
|
-
from adam.commands.postgres.postgres_session import PostgresSession
|
|
14
12
|
from adam.config import Config
|
|
15
|
-
from adam.
|
|
16
|
-
from adam.
|
|
13
|
+
from adam.utils_audits import Audits
|
|
14
|
+
from adam.utils_k8s.kube_context import KubeContext
|
|
17
15
|
from adam.log import Log
|
|
18
16
|
from adam.repl_commands import ReplCommands
|
|
19
17
|
from adam.repl_session import ReplSession
|
|
20
18
|
from adam.repl_state import ReplState
|
|
21
|
-
from adam.utils import
|
|
19
|
+
from adam.utils import clear_wait_log_flag, debug_trace, deep_sort_dict, tabulize, log2, log_exc, log_timing
|
|
22
20
|
from adam.apps import Apps
|
|
21
|
+
from adam.utils_repl.repl_completer import ReplCompleter, merge_completions
|
|
23
22
|
from . import __version__
|
|
24
23
|
|
|
24
|
+
import nest_asyncio
|
|
25
|
+
nest_asyncio.apply()
|
|
26
|
+
|
|
27
|
+
import asyncio
|
|
28
|
+
|
|
25
29
|
def enter_repl(state: ReplState):
|
|
26
30
|
if os.getenv('QING_DROPPED', 'false') == 'true':
|
|
27
31
|
log2('You have dropped to bash from another qing instance. Please enter "exit" to go back to qing.')
|
|
@@ -33,54 +37,13 @@ def enter_repl(state: ReplState):
|
|
|
33
37
|
session = ReplSession().prompt_session
|
|
34
38
|
|
|
35
39
|
def prompt_msg():
|
|
36
|
-
msg =
|
|
37
|
-
if state.device == ReplState.P:
|
|
38
|
-
msg = f'{ReplState.P}:'
|
|
39
|
-
pg = PostgresSession(state.namespace, state.pg_path) if state.pg_path else None
|
|
40
|
-
if pg and pg.db:
|
|
41
|
-
msg += pg.db
|
|
42
|
-
elif pg and pg.host:
|
|
43
|
-
msg += pg.host
|
|
44
|
-
elif state.device == ReplState.A:
|
|
45
|
-
msg = f'{ReplState.A}:'
|
|
46
|
-
if state.app_env:
|
|
47
|
-
msg += state.app_env
|
|
48
|
-
if state.app_app:
|
|
49
|
-
msg += f'/{state.app_app}'
|
|
50
|
-
else:
|
|
51
|
-
msg = f'{ReplState.C}:'
|
|
52
|
-
if state.pod:
|
|
53
|
-
# cs-d0767a536f-cs-d0767a536f-default-sts-0
|
|
54
|
-
group = re.match(r".*?-.*?-(.*)", state.pod)
|
|
55
|
-
msg += group[1]
|
|
56
|
-
elif state.sts:
|
|
57
|
-
# cs-d0767a536f-cs-d0767a536f-default-sts
|
|
58
|
-
group = re.match(r".*?-.*?-(.*)", state.sts)
|
|
59
|
-
msg += group[1]
|
|
40
|
+
msg = state.__str__()
|
|
60
41
|
|
|
61
42
|
return f"{msg}$ " if state.bash_session else f"{msg}> "
|
|
62
43
|
|
|
63
44
|
Log.log2(f'kaqing {__version__}')
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if state.device == ReplState.C:
|
|
67
|
-
if not ss:
|
|
68
|
-
raise Exception("no Cassandra clusters found")
|
|
69
|
-
elif len(ss) == 1 and Config().get('repl.auto-enter-only-cluster', True):
|
|
70
|
-
cluster = ss[0]
|
|
71
|
-
state.sts = cluster[0]
|
|
72
|
-
state.namespace = cluster[1]
|
|
73
|
-
state.wait_log(f'Moving to the only Cassandra cluster: {state.sts}@{state.namespace}...')
|
|
74
|
-
elif state.device == ReplState.A:
|
|
75
|
-
if app := Config().get('repl.auto-enter-app', 'c3/c3'):
|
|
76
|
-
if app != 'no':
|
|
77
|
-
ea = app.split('/')
|
|
78
|
-
state.app_env = ea[0]
|
|
79
|
-
if len(ea) > 1:
|
|
80
|
-
state.app_app = ea[1]
|
|
81
|
-
state.wait_log(f'Moving to {state.app_env}/{state.app_app}...')
|
|
82
|
-
else:
|
|
83
|
-
state.wait_log(f'Moving to {state.app_env}...')
|
|
45
|
+
|
|
46
|
+
Devices.device(state).enter(state)
|
|
84
47
|
|
|
85
48
|
kb = KeyBindings()
|
|
86
49
|
|
|
@@ -88,72 +51,122 @@ def enter_repl(state: ReplState):
|
|
|
88
51
|
def _(event):
|
|
89
52
|
event.app.current_buffer.text = ''
|
|
90
53
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
54
|
+
with Audits.offload() as exec:
|
|
55
|
+
# warm up AWS lambda - this log line may timeout and get lost, which is fine
|
|
56
|
+
exec.submit(Audits.log, 'entering kaqing repl', state.namespace, 'z', 0.0)
|
|
57
|
+
|
|
58
|
+
s0 = time.time()
|
|
59
|
+
|
|
60
|
+
# use sorted command list only for auto-completion
|
|
61
|
+
sorted_cmds = sorted(cmd_list, key=lambda cmd: cmd.command())
|
|
62
|
+
while True:
|
|
63
|
+
cmd: str = None
|
|
64
|
+
result = None
|
|
65
|
+
try:
|
|
66
|
+
completer = ReplCompleter.from_nested_dict({})
|
|
67
|
+
if not state.bash_session:
|
|
68
|
+
with log_timing('completion-calcs'):
|
|
69
|
+
completions = {}
|
|
70
|
+
# app commands are available only on a: drive
|
|
71
|
+
if state.device == ReplState.A and state.app_app:
|
|
72
|
+
completions = log_timing('actions', lambda: Apps(path='apps.yaml').commands())
|
|
73
|
+
|
|
74
|
+
for c in sorted_cmds:
|
|
75
|
+
with log_exc(f'* {c.command()} command returned None completions.'):
|
|
76
|
+
completions = log_timing(c.command(), lambda: deep_sort_dict(merge_completions(completions, c.completion(state))))
|
|
77
|
+
|
|
78
|
+
# print(json.dumps(completions, indent=4))
|
|
79
|
+
completer = ReplCompleter.from_nested_dict(completions)
|
|
80
|
+
|
|
81
|
+
cmd = session.prompt(prompt_msg(), completer=completer, key_bindings=kb)
|
|
82
|
+
s0 = time.time()
|
|
83
|
+
|
|
84
|
+
if state.bash_session:
|
|
85
|
+
if cmd.strip(' ') == 'exit':
|
|
86
|
+
state.exit_bash()
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
cmd = f'bash {cmd}'
|
|
90
|
+
|
|
91
|
+
def targetted(state: ReplState, cmd: str):
|
|
92
|
+
if not (cmd.startswith('@') and len(arry := cmd.split(' ')) > 1):
|
|
93
|
+
return state, cmd
|
|
94
|
+
|
|
95
|
+
if state.device == ReplState.A and state.app_app or state.device == ReplState.P:
|
|
96
|
+
state.push(pod_targetted=True)
|
|
97
|
+
|
|
98
|
+
state.app_pod = arry[0].strip('@')
|
|
99
|
+
cmd = ' '.join(arry[1:])
|
|
100
|
+
elif state.device == ReplState.P:
|
|
101
|
+
state.push(pod_targetted=True)
|
|
102
|
+
|
|
103
|
+
state.app_pod = arry[0].strip('@')
|
|
104
|
+
cmd = ' '.join(arry[1:])
|
|
105
|
+
elif state.sts:
|
|
106
|
+
state.push(pod_targetted=True)
|
|
107
|
+
|
|
108
|
+
state.pod = arry[0].strip('@')
|
|
109
|
+
cmd = ' '.join(arry[1:])
|
|
110
|
+
|
|
111
|
+
return (state, cmd)
|
|
112
|
+
|
|
113
|
+
target, cmd = targetted(state, cmd)
|
|
114
|
+
try:
|
|
115
|
+
if cmd and cmd.strip(' ') and not (result := cmds.run(cmd, target)):
|
|
116
|
+
result = try_device_default_action(target, cmds, cmd_list, cmd)
|
|
117
|
+
except InvalidStateException:
|
|
118
|
+
pass
|
|
119
|
+
except InvalidArgumentsException:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
if result and type(result) is ReplState and (s := cast(ReplState, result).export_session) != state.export_session:
|
|
123
|
+
state.export_session = s
|
|
124
|
+
|
|
125
|
+
except EOFError: # Handle Ctrl+D (EOF) for graceful exit
|
|
126
|
+
break
|
|
127
|
+
except Exception as e:
|
|
128
|
+
if Config().get('debugs.exit-on-error', False):
|
|
129
|
+
raise e
|
|
130
|
+
else:
|
|
131
|
+
log2(e)
|
|
132
|
+
debug_trace()
|
|
133
|
+
finally:
|
|
134
|
+
if not state.bash_session:
|
|
135
|
+
state.pop()
|
|
136
|
+
|
|
137
|
+
clear_wait_log_flag()
|
|
138
|
+
if cmd:
|
|
139
|
+
log_timing(f'command {cmd}', s0=s0)
|
|
140
|
+
|
|
141
|
+
# offload audit logging
|
|
142
|
+
if cmd and (state.device != ReplState.L or Config().get('audit.log-audit-queries', False)):
|
|
143
|
+
exec.submit(Audits.log, cmd, state.namespace, state.device, time.time() - s0, get_audit_extra(result))
|
|
144
|
+
|
|
145
|
+
def try_device_default_action(state: ReplState, cmds: Command, cmd_list: list[Command], cmd: str):
|
|
146
|
+
action_taken, result = Devices.device(state).try_fallback_action(cmds, state, cmd)
|
|
147
|
+
|
|
148
|
+
if not action_taken:
|
|
149
|
+
log2(f'* Invalid command: {cmd}')
|
|
150
|
+
log2()
|
|
151
|
+
tabulize([c.help(state) for c in cmd_list if c.help(state)], separator='\t', to=2)
|
|
152
|
+
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
def get_audit_extra(result: any):
|
|
156
|
+
if not result:
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
if type(result) is list:
|
|
160
|
+
extras = set()
|
|
161
|
+
|
|
162
|
+
for r in result:
|
|
163
|
+
if hasattr(r, '__audit_extra__') and (x := r.__audit_extra__()):
|
|
164
|
+
extras.add(x)
|
|
165
|
+
|
|
166
|
+
return ','.join(list(extras))
|
|
167
|
+
|
|
168
|
+
if hasattr(result, '__audit_extra__') and (x := result.__audit_extra__()):
|
|
169
|
+
return x
|
|
157
170
|
|
|
158
171
|
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=ClusterCommandHelper, help="Enter interactive shell.")
|
|
159
172
|
@click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
|
|
@@ -167,6 +180,9 @@ def repl(kubeconfig: str, config: str, param: list[str], cluster:str, namespace:
|
|
|
167
180
|
if not KubeContext.init_params(config, param):
|
|
168
181
|
return
|
|
169
182
|
|
|
170
|
-
state = ReplState(
|
|
171
|
-
state, _ = state.
|
|
183
|
+
state = ReplState(ns_sts=cluster, namespace=namespace, in_repl=True)
|
|
184
|
+
state, _ = state.apply_device_arg(extra_args)
|
|
185
|
+
if not state.device:
|
|
186
|
+
state.device=Config().get('repl.start-drive', 'a')
|
|
187
|
+
|
|
172
188
|
enter_repl(state)
|