kaqing 1.77.0__py3-none-any.whl → 2.0.171__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.
- adam/__init__.py +1 -0
- adam/app_session.py +182 -0
- {walker → adam}/apps.py +8 -24
- {walker → adam}/batch.py +54 -97
- {walker → adam}/checks/check.py +3 -3
- {walker → adam}/checks/check_result.py +1 -1
- adam/checks/check_utils.py +65 -0
- {walker → adam}/checks/compactionstats.py +6 -6
- {walker → adam}/checks/cpu.py +14 -8
- adam/checks/cpu_metrics.py +52 -0
- {walker → adam}/checks/disk.py +6 -6
- {walker → adam}/checks/gossip.py +5 -5
- {walker → adam}/checks/memory.py +7 -7
- {walker → adam}/checks/status.py +5 -5
- {walker → adam}/cli.py +3 -3
- {walker → adam}/columns/column.py +1 -1
- adam/columns/columns.py +45 -0
- {walker → adam}/columns/compactions.py +5 -5
- {walker → adam}/columns/cpu.py +6 -4
- adam/columns/cpu_metrics.py +22 -0
- {walker → adam}/columns/dir_data.py +3 -3
- {walker → adam}/columns/dir_snapshots.py +3 -3
- {walker → adam}/columns/gossip.py +5 -5
- {walker → adam}/columns/host_id.py +3 -3
- {walker → adam}/columns/memory.py +3 -3
- {walker → adam}/columns/node_address.py +3 -3
- {walker → adam}/columns/node_load.py +3 -3
- {walker → adam}/columns/node_owns.py +3 -3
- {walker → adam}/columns/node_status.py +3 -3
- {walker → adam}/columns/node_tokens.py +3 -3
- {walker → adam}/columns/node_utils.py +2 -2
- {walker → adam}/columns/pod_name.py +2 -2
- {walker → adam}/columns/volume_cassandra.py +4 -4
- {walker → adam}/columns/volume_root.py +3 -3
- adam/commands/__init__.py +15 -0
- adam/commands/alter_tables.py +81 -0
- adam/commands/app_cmd.py +38 -0
- {walker → adam}/commands/app_ping.py +10 -16
- adam/commands/audit/audit.py +84 -0
- adam/commands/audit/audit_repair_tables.py +74 -0
- adam/commands/audit/audit_run.py +50 -0
- adam/commands/audit/show_last10.py +48 -0
- adam/commands/audit/show_slow10.py +47 -0
- adam/commands/audit/show_top10.py +45 -0
- adam/commands/audit/utils_show_top10.py +59 -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 +50 -0
- adam/commands/cd.py +43 -0
- adam/commands/check.py +73 -0
- {walker → adam}/commands/cli_commands.py +7 -8
- adam/commands/code.py +57 -0
- adam/commands/command.py +190 -0
- {walker → adam}/commands/command_helpers.py +1 -1
- {walker → adam}/commands/commands_utils.py +15 -25
- adam/commands/cp.py +89 -0
- adam/commands/cql/cql_completions.py +33 -0
- {walker/commands → adam/commands/cql}/cqlsh.py +20 -35
- adam/commands/cql/utils_cql.py +343 -0
- {walker/commands/frontend → adam/commands/deploy}/code_start.py +11 -14
- adam/commands/deploy/code_stop.py +40 -0
- {walker/commands/frontend → adam/commands/deploy}/code_utils.py +7 -9
- adam/commands/deploy/deploy.py +25 -0
- adam/commands/deploy/deploy_frontend.py +49 -0
- adam/commands/deploy/deploy_pg_agent.py +35 -0
- adam/commands/deploy/deploy_pod.py +108 -0
- adam/commands/deploy/deploy_utils.py +29 -0
- adam/commands/deploy/undeploy.py +25 -0
- adam/commands/deploy/undeploy_frontend.py +38 -0
- adam/commands/deploy/undeploy_pg_agent.py +39 -0
- adam/commands/deploy/undeploy_pod.py +48 -0
- adam/commands/devices/device.py +118 -0
- adam/commands/devices/device_app.py +173 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +185 -0
- adam/commands/devices/device_export.py +86 -0
- adam/commands/devices/device_postgres.py +144 -0
- adam/commands/devices/devices.py +25 -0
- {walker → adam}/commands/exit.py +3 -6
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +51 -0
- adam/commands/export/drop_export_database.py +55 -0
- adam/commands/export/drop_export_databases.py +43 -0
- adam/commands/export/export.py +53 -0
- adam/commands/export/export_databases.py +170 -0
- adam/commands/export/export_handlers.py +71 -0
- adam/commands/export/export_select.py +81 -0
- adam/commands/export/export_select_x.py +54 -0
- adam/commands/export/export_use.py +52 -0
- adam/commands/export/exporter.py +352 -0
- adam/commands/export/import_session.py +40 -0
- adam/commands/export/importer.py +67 -0
- adam/commands/export/importer_athena.py +80 -0
- adam/commands/export/importer_sqlite.py +47 -0
- adam/commands/export/show_column_counts.py +54 -0
- adam/commands/export/show_export_databases.py +36 -0
- adam/commands/export/show_export_session.py +48 -0
- adam/commands/export/show_export_sessions.py +44 -0
- adam/commands/export/utils_export.py +314 -0
- {walker → adam}/commands/help.py +17 -12
- adam/commands/intermediate_command.py +49 -0
- adam/commands/issues.py +43 -0
- adam/commands/kubectl.py +38 -0
- adam/commands/login.py +70 -0
- {walker → adam}/commands/logs.py +8 -10
- adam/commands/ls.py +41 -0
- adam/commands/medusa/medusa.py +27 -0
- adam/commands/medusa/medusa_backup.py +57 -0
- adam/commands/medusa/medusa_restore.py +83 -0
- adam/commands/medusa/medusa_show_backupjobs.py +51 -0
- adam/commands/medusa/medusa_show_restorejobs.py +47 -0
- {walker → adam}/commands/nodetool.py +17 -21
- {walker → adam}/commands/param_get.py +15 -16
- adam/commands/param_set.py +43 -0
- adam/commands/postgres/postgres.py +104 -0
- adam/commands/postgres/postgres_context.py +274 -0
- {walker → adam}/commands/postgres/postgres_ls.py +7 -11
- {walker → adam}/commands/postgres/postgres_preview.py +8 -13
- adam/commands/postgres/psql_completions.py +10 -0
- adam/commands/postgres/utils_postgres.py +66 -0
- adam/commands/preview_table.py +37 -0
- adam/commands/pwd.py +47 -0
- adam/commands/reaper/reaper.py +35 -0
- adam/commands/reaper/reaper_forward.py +93 -0
- adam/commands/reaper/reaper_forward_session.py +6 -0
- {walker → adam}/commands/reaper/reaper_forward_stop.py +13 -19
- {walker → adam}/commands/reaper/reaper_restart.py +10 -17
- adam/commands/reaper/reaper_run_abort.py +46 -0
- adam/commands/reaper/reaper_runs.py +82 -0
- adam/commands/reaper/reaper_runs_abort.py +63 -0
- adam/commands/reaper/reaper_schedule_activate.py +45 -0
- adam/commands/reaper/reaper_schedule_start.py +45 -0
- adam/commands/reaper/reaper_schedule_stop.py +45 -0
- {walker → adam}/commands/reaper/reaper_schedules.py +6 -16
- {walker → adam}/commands/reaper/reaper_status.py +11 -19
- adam/commands/reaper/utils_reaper.py +196 -0
- adam/commands/repair/repair.py +26 -0
- {walker → adam}/commands/repair/repair_log.py +7 -10
- adam/commands/repair/repair_run.py +70 -0
- adam/commands/repair/repair_scan.py +71 -0
- {walker → adam}/commands/repair/repair_stop.py +8 -11
- adam/commands/report.py +61 -0
- adam/commands/restart.py +60 -0
- {walker → adam}/commands/rollout.py +25 -30
- adam/commands/shell.py +34 -0
- adam/commands/show/show.py +39 -0
- walker/commands/show/show_version.py → adam/commands/show/show_adam.py +14 -10
- adam/commands/show/show_app_actions.py +57 -0
- {walker → adam}/commands/show/show_app_id.py +12 -15
- {walker → adam}/commands/show/show_app_queues.py +9 -12
- adam/commands/show/show_cassandra_repairs.py +38 -0
- adam/commands/show/show_cassandra_status.py +124 -0
- {walker → adam}/commands/show/show_cassandra_version.py +6 -16
- adam/commands/show/show_commands.py +59 -0
- walker/commands/show/show_storage.py → adam/commands/show/show_host.py +11 -13
- adam/commands/show/show_login.py +62 -0
- {walker → adam}/commands/show/show_params.py +4 -4
- adam/commands/show/show_processes.py +51 -0
- adam/commands/show/show_storage.py +42 -0
- adam/commands/watch.py +82 -0
- {walker → adam}/config.py +10 -22
- {walker → adam}/embedded_apps.py +1 -1
- adam/embedded_params.py +2 -0
- adam/log.py +47 -0
- {walker → adam}/pod_exec_result.py +10 -2
- adam/repl.py +182 -0
- adam/repl_commands.py +124 -0
- adam/repl_state.py +458 -0
- adam/sql/__init__.py +0 -0
- adam/sql/sql_completer.py +120 -0
- adam/sql/sql_state_machine.py +618 -0
- adam/sql/term_completer.py +76 -0
- adam/sso/__init__.py +0 -0
- {walker → adam}/sso/authenticator.py +5 -1
- adam/sso/authn_ad.py +170 -0
- {walker → adam}/sso/authn_okta.py +39 -22
- adam/sso/cred_cache.py +60 -0
- adam/sso/id_token.py +23 -0
- adam/sso/idp.py +143 -0
- adam/sso/idp_login.py +50 -0
- adam/sso/idp_session.py +55 -0
- adam/sso/sso_config.py +63 -0
- adam/utils.py +679 -0
- adam/utils_app.py +98 -0
- adam/utils_athena.py +145 -0
- adam/utils_audits.py +106 -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 +33 -0
- adam/utils_k8s/cassandra_clusters.py +36 -0
- adam/utils_k8s/cassandra_nodes.py +33 -0
- adam/utils_k8s/config_maps.py +34 -0
- {walker/k8s_utils → adam/utils_k8s}/custom_resources.py +7 -2
- adam/utils_k8s/deployment.py +56 -0
- {walker/k8s_utils → adam/utils_k8s}/ingresses.py +3 -4
- {walker/k8s_utils → adam/utils_k8s}/jobs.py +3 -3
- adam/utils_k8s/k8s.py +87 -0
- {walker/k8s_utils → adam/utils_k8s}/kube_context.py +4 -4
- adam/utils_k8s/pods.py +290 -0
- {walker/k8s_utils → adam/utils_k8s}/secrets.py +8 -4
- adam/utils_k8s/service_accounts.py +170 -0
- {walker/k8s_utils → adam/utils_k8s}/services.py +3 -4
- {walker/k8s_utils → adam/utils_k8s}/statefulsets.py +6 -16
- {walker/k8s_utils → adam/utils_k8s}/volumes.py +10 -1
- adam/utils_net.py +24 -0
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +46 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +109 -0
- adam/version.py +5 -0
- {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/METADATA +1 -1
- kaqing-2.0.171.dist-info/RECORD +236 -0
- kaqing-2.0.171.dist-info/entry_points.txt +3 -0
- kaqing-2.0.171.dist-info/top_level.txt +1 -0
- kaqing-1.77.0.dist-info/RECORD +0 -159
- kaqing-1.77.0.dist-info/entry_points.txt +0 -3
- kaqing-1.77.0.dist-info/top_level.txt +0 -1
- walker/__init__.py +0 -3
- walker/app_session.py +0 -168
- walker/checks/check_utils.py +0 -97
- walker/columns/columns.py +0 -43
- walker/commands/add_user.py +0 -68
- walker/commands/app.py +0 -67
- walker/commands/bash.py +0 -87
- walker/commands/cd.py +0 -115
- walker/commands/check.py +0 -68
- walker/commands/command.py +0 -104
- walker/commands/cp.py +0 -95
- walker/commands/cql_utils.py +0 -53
- walker/commands/devices.py +0 -89
- walker/commands/frontend/code_stop.py +0 -57
- walker/commands/frontend/setup.py +0 -60
- walker/commands/frontend/setup_frontend.py +0 -58
- walker/commands/frontend/teardown.py +0 -61
- walker/commands/frontend/teardown_frontend.py +0 -42
- walker/commands/issues.py +0 -69
- walker/commands/login.py +0 -72
- walker/commands/ls.py +0 -145
- walker/commands/medusa/medusa.py +0 -69
- walker/commands/medusa/medusa_backup.py +0 -61
- walker/commands/medusa/medusa_restore.py +0 -86
- walker/commands/medusa/medusa_show_backupjobs.py +0 -52
- walker/commands/medusa/medusa_show_restorejobs.py +0 -52
- walker/commands/param_set.py +0 -44
- walker/commands/postgres/postgres.py +0 -113
- walker/commands/postgres/postgres_session.py +0 -225
- walker/commands/preview_table.py +0 -98
- walker/commands/processes.py +0 -53
- walker/commands/pwd.py +0 -64
- walker/commands/reaper/reaper.py +0 -78
- walker/commands/reaper/reaper_forward.py +0 -100
- walker/commands/reaper/reaper_run_abort.py +0 -65
- walker/commands/reaper/reaper_runs.py +0 -97
- walker/commands/reaper/reaper_runs_abort.py +0 -83
- walker/commands/reaper/reaper_schedule_activate.py +0 -64
- walker/commands/reaper/reaper_schedule_start.py +0 -64
- walker/commands/reaper/reaper_schedule_stop.py +0 -64
- walker/commands/reaper/reaper_session.py +0 -159
- walker/commands/repair/repair.py +0 -68
- walker/commands/repair/repair_run.py +0 -72
- walker/commands/repair/repair_scan.py +0 -79
- walker/commands/report.py +0 -57
- walker/commands/restart.py +0 -61
- walker/commands/show/show.py +0 -72
- walker/commands/show/show_app_actions.py +0 -53
- walker/commands/show/show_cassandra_status.py +0 -35
- walker/commands/show/show_commands.py +0 -58
- walker/commands/show/show_processes.py +0 -35
- walker/commands/show/show_repairs.py +0 -47
- walker/commands/status.py +0 -128
- walker/commands/storage.py +0 -52
- walker/commands/user_entry.py +0 -69
- walker/commands/watch.py +0 -85
- walker/embedded_params.py +0 -2
- walker/k8s_utils/cassandra_clusters.py +0 -48
- walker/k8s_utils/cassandra_nodes.py +0 -26
- walker/k8s_utils/pods.py +0 -211
- walker/repl.py +0 -165
- walker/repl_commands.py +0 -58
- walker/repl_state.py +0 -211
- walker/sso/authn_ad.py +0 -94
- walker/sso/idp.py +0 -150
- walker/sso/idp_login.py +0 -29
- walker/sso/sso_config.py +0 -45
- walker/utils.py +0 -194
- walker/version.py +0 -5
- {walker → adam}/checks/__init__.py +0 -0
- {walker → adam}/checks/check_context.py +0 -0
- {walker → adam}/checks/issue.py +0 -0
- {walker → adam}/cli_group.py +0 -0
- {walker → adam}/columns/__init__.py +0 -0
- {walker/commands → adam/commands/audit}/__init__.py +0 -0
- {walker/commands/frontend → adam/commands/cql}/__init__.py +0 -0
- {walker/commands/medusa → adam/commands/deploy}/__init__.py +0 -0
- {walker/commands/postgres → adam/commands/devices}/__init__.py +0 -0
- {walker/commands/reaper → adam/commands/export}/__init__.py +0 -0
- {walker/commands/repair → adam/commands/medusa}/__init__.py +0 -0
- {walker → adam}/commands/nodetool_commands.py +0 -0
- {walker/commands/show → adam/commands/postgres}/__init__.py +0 -0
- {walker/k8s_utils → adam/commands/reaper}/__init__.py +0 -0
- {walker/sso → adam/commands/repair}/__init__.py +0 -0
- /walker/medusa_show_restorejobs.py → /adam/commands/show/__init__.py +0 -0
- {walker → adam}/repl_session.py +0 -0
- {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/WHEEL +0 -0
adam/repl_state.py
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
from copy import copy
|
|
2
|
+
from enum import Enum
|
|
3
|
+
import re
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from adam.commands.postgres.postgres_context import PostgresContext
|
|
7
|
+
from adam.utils_k8s.app_clusters import AppClusters
|
|
8
|
+
from adam.utils_k8s.app_pods import AppPods
|
|
9
|
+
from adam.utils_k8s.cassandra_clusters import CassandraClusters
|
|
10
|
+
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
11
|
+
from adam.utils_k8s.kube_context import KubeContext
|
|
12
|
+
from adam.utils_k8s.secrets import Secrets
|
|
13
|
+
from adam.utils import display_help, log2, random_alphanumeric
|
|
14
|
+
|
|
15
|
+
class BashSession:
|
|
16
|
+
def __init__(self, device: str = None):
|
|
17
|
+
self.session_id = random_alphanumeric(6)
|
|
18
|
+
self.device = device
|
|
19
|
+
|
|
20
|
+
def pwd(self, state: 'ReplState'):
|
|
21
|
+
command = f'cat /tmp/.qing-{self.session_id}'
|
|
22
|
+
|
|
23
|
+
with device(state) as pods:
|
|
24
|
+
return pods.exec(command, action='bash', show_out=False)
|
|
25
|
+
|
|
26
|
+
class RequiredState(Enum):
|
|
27
|
+
CLUSTER = 'cluster'
|
|
28
|
+
POD = 'pod'
|
|
29
|
+
CLUSTER_OR_POD = 'cluster_or_pod'
|
|
30
|
+
NAMESPACE = 'namespace'
|
|
31
|
+
PG_DATABASE = 'pg_database'
|
|
32
|
+
APP_APP = 'app_app'
|
|
33
|
+
EXPORT_DB = 'export_db'
|
|
34
|
+
|
|
35
|
+
class ReplState:
|
|
36
|
+
A = 'a'
|
|
37
|
+
C = 'c'
|
|
38
|
+
L = 'l'
|
|
39
|
+
P = 'p'
|
|
40
|
+
X = 'x'
|
|
41
|
+
|
|
42
|
+
ANY = [A, C, L, P, X]
|
|
43
|
+
NON_L = [A, C, P, X]
|
|
44
|
+
|
|
45
|
+
def __init__(self, device: str = None,
|
|
46
|
+
sts: str = None, pod: str = None, namespace: str = None, ns_sts: str = None,
|
|
47
|
+
pg_path: str = None,
|
|
48
|
+
app_env: str = None, app_app: str = None, app_pod: str = None,
|
|
49
|
+
in_repl = False, bash_session: BashSession = None, remote_dir = None):
|
|
50
|
+
self.namespace = KubeContext.in_cluster_namespace()
|
|
51
|
+
|
|
52
|
+
self.device = device
|
|
53
|
+
self.sts = sts
|
|
54
|
+
self.pod = pod
|
|
55
|
+
self.pg_path = pg_path
|
|
56
|
+
self.app_env = app_env
|
|
57
|
+
self.app_app = app_app
|
|
58
|
+
self.app_pod = app_pod
|
|
59
|
+
if namespace:
|
|
60
|
+
self.namespace = namespace
|
|
61
|
+
self.in_repl = in_repl
|
|
62
|
+
self.bash_session = bash_session
|
|
63
|
+
self.remote_dir = remote_dir
|
|
64
|
+
self.original_state: ReplState = None
|
|
65
|
+
|
|
66
|
+
self.export_session: str = None
|
|
67
|
+
|
|
68
|
+
if ns_sts:
|
|
69
|
+
nn = ns_sts.split('@')
|
|
70
|
+
self.sts = nn[0]
|
|
71
|
+
if len(nn) > 1:
|
|
72
|
+
self.namespace = nn[1]
|
|
73
|
+
|
|
74
|
+
# work for CliCommand.Values()
|
|
75
|
+
def __eq__(self, other: 'ReplState'):
|
|
76
|
+
return self.sts == other.sts and self.pod == other.pod
|
|
77
|
+
|
|
78
|
+
def __hash__(self):
|
|
79
|
+
return hash((self.sts, self.pod))
|
|
80
|
+
|
|
81
|
+
def __str__(self):
|
|
82
|
+
msg = ''
|
|
83
|
+
if self.device == ReplState.P:
|
|
84
|
+
msg = f'{ReplState.P}:'
|
|
85
|
+
pg: PostgresContext = PostgresContext.apply(self.namespace, self.pg_path) if self.pg_path else None
|
|
86
|
+
if pg and pg.db:
|
|
87
|
+
msg += pg.db
|
|
88
|
+
elif pg and pg.host:
|
|
89
|
+
msg += pg.host
|
|
90
|
+
elif self.device == ReplState.A:
|
|
91
|
+
msg = f'{ReplState.A}:'
|
|
92
|
+
if self.app_env:
|
|
93
|
+
msg += self.app_env
|
|
94
|
+
if self.app_app:
|
|
95
|
+
msg += f'/{self.app_app}'
|
|
96
|
+
if self.app_pod:
|
|
97
|
+
# azops88-c3-c3-k8sdeploy-appleader-001-79957cf5b6-9k4bw
|
|
98
|
+
group = re.match(r".*?-.*?-.*?-.*?-(.*?-.*?)-.*", self.app_pod)
|
|
99
|
+
msg += '/' + group[1]
|
|
100
|
+
elif self.device == ReplState.L:
|
|
101
|
+
msg = f'{ReplState.L}:'
|
|
102
|
+
elif self.device == ReplState.X:
|
|
103
|
+
msg = f'{ReplState.X}:'
|
|
104
|
+
if self.export_session:
|
|
105
|
+
msg += self.export_session
|
|
106
|
+
else:
|
|
107
|
+
msg = f'{ReplState.C}:'
|
|
108
|
+
if self.pod:
|
|
109
|
+
# cs-d0767a536f-cs-d0767a536f-default-sts-0
|
|
110
|
+
group = re.match(r".*?-.*?-(.*)", self.pod)
|
|
111
|
+
msg += group[1]
|
|
112
|
+
elif self.sts:
|
|
113
|
+
# cs-d0767a536f-cs-d0767a536f-default-sts
|
|
114
|
+
group = re.match(r".*?-.*?-(.*)", self.sts)
|
|
115
|
+
msg += group[1]
|
|
116
|
+
|
|
117
|
+
return msg
|
|
118
|
+
|
|
119
|
+
def apply_args(self, args: list[str], cmd: list[str] = None, resolve_pg = True, args_to_check = 6) -> tuple['ReplState', list[str]]:
|
|
120
|
+
state = self
|
|
121
|
+
|
|
122
|
+
new_args = []
|
|
123
|
+
for index, arg in enumerate(args):
|
|
124
|
+
if index < args_to_check:
|
|
125
|
+
state = copy(state)
|
|
126
|
+
|
|
127
|
+
s, n = KubeContext.is_sts_name(arg)
|
|
128
|
+
if s:
|
|
129
|
+
if not state.sts:
|
|
130
|
+
state.sts = s
|
|
131
|
+
if n and not state.namespace:
|
|
132
|
+
state.namespace = n
|
|
133
|
+
|
|
134
|
+
p, n = KubeContext.is_pod_name(arg)
|
|
135
|
+
if p:
|
|
136
|
+
if not state.pod:
|
|
137
|
+
state.pod = p
|
|
138
|
+
if n and not state.namespace:
|
|
139
|
+
state.namespace = n
|
|
140
|
+
|
|
141
|
+
pg = None
|
|
142
|
+
if resolve_pg:
|
|
143
|
+
pg = KubeContext.is_pg_name(arg)
|
|
144
|
+
if pg and not state.pg_path:
|
|
145
|
+
state.pg_path = pg
|
|
146
|
+
|
|
147
|
+
if not s and not p and not pg:
|
|
148
|
+
new_args.append(arg)
|
|
149
|
+
else:
|
|
150
|
+
new_args.append(arg)
|
|
151
|
+
|
|
152
|
+
if cmd:
|
|
153
|
+
new_args = new_args[len(cmd):]
|
|
154
|
+
|
|
155
|
+
return (state, new_args)
|
|
156
|
+
|
|
157
|
+
def apply_device_arg(self, args: list[str], cmd: list[str] = None) -> tuple['ReplState', list[str]]:
|
|
158
|
+
state = self
|
|
159
|
+
|
|
160
|
+
new_args = []
|
|
161
|
+
for index, arg in enumerate(args):
|
|
162
|
+
if index < 6:
|
|
163
|
+
state = copy(state)
|
|
164
|
+
|
|
165
|
+
groups = re.match(r'^([a|c|l|p|x]):(.*)$', arg)
|
|
166
|
+
if groups:
|
|
167
|
+
if groups[1] == 'p':
|
|
168
|
+
state.device = 'p'
|
|
169
|
+
state.pg_path = groups[2]
|
|
170
|
+
elif groups[1] == 'c':
|
|
171
|
+
state.device = 'c'
|
|
172
|
+
if path := groups[2]:
|
|
173
|
+
p_and_ns = path.split('@')
|
|
174
|
+
sts_and_pod = p_and_ns[0].split('/')
|
|
175
|
+
state.sts = sts_and_pod[0]
|
|
176
|
+
if len(sts_and_pod) > 1:
|
|
177
|
+
state.pod = sts_and_pod[1]
|
|
178
|
+
if len(p_and_ns) > 1:
|
|
179
|
+
state.namespace = p_and_ns[1]
|
|
180
|
+
elif ns := KubeContext.in_cluster_namespace():
|
|
181
|
+
state.namespace = ns
|
|
182
|
+
elif groups[1] == 'l':
|
|
183
|
+
state.device = 'l'
|
|
184
|
+
elif groups[1] == 'x':
|
|
185
|
+
state.device = 'x'
|
|
186
|
+
else:
|
|
187
|
+
state.device = 'a'
|
|
188
|
+
if path := groups[2]:
|
|
189
|
+
env_and_app = path.split('/')
|
|
190
|
+
state.app_env = env_and_app[0]
|
|
191
|
+
if len(env_and_app) > 1:
|
|
192
|
+
state.app_app = env_and_app[1]
|
|
193
|
+
else:
|
|
194
|
+
new_args.append(arg)
|
|
195
|
+
else:
|
|
196
|
+
new_args.append(arg)
|
|
197
|
+
|
|
198
|
+
if cmd:
|
|
199
|
+
new_args = new_args[len(cmd):]
|
|
200
|
+
|
|
201
|
+
return (state, new_args)
|
|
202
|
+
|
|
203
|
+
def validate(self, required: list[RequiredState] = [], show_err = True):
|
|
204
|
+
if not required:
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
def default_err():
|
|
208
|
+
if self.in_repl:
|
|
209
|
+
log2(f'Not a valid command on {self.device}: drive.')
|
|
210
|
+
else:
|
|
211
|
+
log2('* on a wrong device.')
|
|
212
|
+
log2()
|
|
213
|
+
display_help()
|
|
214
|
+
|
|
215
|
+
if type(required) is not list:
|
|
216
|
+
valid, err = self._validate(required)
|
|
217
|
+
if valid:
|
|
218
|
+
return True
|
|
219
|
+
|
|
220
|
+
if show_err:
|
|
221
|
+
if err:
|
|
222
|
+
err()
|
|
223
|
+
else:
|
|
224
|
+
default_err()
|
|
225
|
+
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
devices = [r for r in required if r in [ReplState.L, ReplState.A, ReplState.C, ReplState.P, ReplState.X]]
|
|
229
|
+
non_devices = [r for r in required if r not in [ReplState.L, ReplState.A, ReplState.C, ReplState.P, ReplState.X]]
|
|
230
|
+
|
|
231
|
+
first_error: Callable = None
|
|
232
|
+
for r in non_devices:
|
|
233
|
+
valid, err = self._validate(r)
|
|
234
|
+
if valid:
|
|
235
|
+
return True
|
|
236
|
+
|
|
237
|
+
if not first_error:
|
|
238
|
+
first_error = err
|
|
239
|
+
|
|
240
|
+
if devices:
|
|
241
|
+
valid, err = self._validate_device(devices)
|
|
242
|
+
if valid:
|
|
243
|
+
return True
|
|
244
|
+
|
|
245
|
+
if not first_error:
|
|
246
|
+
first_error = err
|
|
247
|
+
|
|
248
|
+
if show_err and first_error:
|
|
249
|
+
if first_error:
|
|
250
|
+
first_error()
|
|
251
|
+
else:
|
|
252
|
+
default_err()
|
|
253
|
+
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
def _validate(self, required: RequiredState):
|
|
257
|
+
if required == RequiredState.CLUSTER:
|
|
258
|
+
if self.device != ReplState.C:
|
|
259
|
+
return (False, None)
|
|
260
|
+
|
|
261
|
+
if not self.namespace or not self.sts:
|
|
262
|
+
def error():
|
|
263
|
+
if self.in_repl:
|
|
264
|
+
log2('cd to a Cassandra cluster first.')
|
|
265
|
+
else:
|
|
266
|
+
log2('* Cassandra cluster is missing.')
|
|
267
|
+
log2()
|
|
268
|
+
display_help()
|
|
269
|
+
return (False, error)
|
|
270
|
+
|
|
271
|
+
elif required == RequiredState.POD:
|
|
272
|
+
if self.device != ReplState.C:
|
|
273
|
+
return (False, None)
|
|
274
|
+
|
|
275
|
+
if not self.namespace or not self.pod:
|
|
276
|
+
def error():
|
|
277
|
+
if self.in_repl:
|
|
278
|
+
log2('cd to a pod first.')
|
|
279
|
+
else:
|
|
280
|
+
log2('* Pod is missing.')
|
|
281
|
+
log2()
|
|
282
|
+
display_help()
|
|
283
|
+
return (False, error)
|
|
284
|
+
|
|
285
|
+
elif required == RequiredState.CLUSTER_OR_POD:
|
|
286
|
+
if self.device != ReplState.C:
|
|
287
|
+
return (False, None)
|
|
288
|
+
|
|
289
|
+
if not self.namespace or not self.sts and not self.pod:
|
|
290
|
+
def error():
|
|
291
|
+
if self.in_repl:
|
|
292
|
+
log2('cd to a Cassandra cluster first.')
|
|
293
|
+
else:
|
|
294
|
+
log2('* Cassandra cluster or pod is missing.')
|
|
295
|
+
log2()
|
|
296
|
+
display_help()
|
|
297
|
+
return (False, error)
|
|
298
|
+
|
|
299
|
+
elif required == RequiredState.NAMESPACE:
|
|
300
|
+
if self.device != ReplState.C:
|
|
301
|
+
return (False, None)
|
|
302
|
+
|
|
303
|
+
if not self.namespace:
|
|
304
|
+
def error():
|
|
305
|
+
if self.in_repl:
|
|
306
|
+
log2('Namespace is required.')
|
|
307
|
+
else:
|
|
308
|
+
log2('* namespace is missing.')
|
|
309
|
+
log2()
|
|
310
|
+
display_help()
|
|
311
|
+
return (False, error)
|
|
312
|
+
|
|
313
|
+
elif required == RequiredState.PG_DATABASE:
|
|
314
|
+
if self.device != ReplState.P:
|
|
315
|
+
return (False, None)
|
|
316
|
+
|
|
317
|
+
pg: PostgresContext = PostgresContext.apply(self.namespace, self.pg_path)
|
|
318
|
+
if not pg.db:
|
|
319
|
+
def error():
|
|
320
|
+
if self.in_repl:
|
|
321
|
+
log2('cd to a database first.')
|
|
322
|
+
else:
|
|
323
|
+
log2('* database is missing.')
|
|
324
|
+
log2()
|
|
325
|
+
display_help()
|
|
326
|
+
return (False, error)
|
|
327
|
+
|
|
328
|
+
elif required == RequiredState.APP_APP:
|
|
329
|
+
if self.device != ReplState.A:
|
|
330
|
+
return (False, None)
|
|
331
|
+
|
|
332
|
+
if not self.app_app:
|
|
333
|
+
def error():
|
|
334
|
+
if self.in_repl:
|
|
335
|
+
log2('cd to an app first.')
|
|
336
|
+
else:
|
|
337
|
+
log2('* app is missing.')
|
|
338
|
+
log2()
|
|
339
|
+
display_help()
|
|
340
|
+
return (False, error)
|
|
341
|
+
|
|
342
|
+
elif required == RequiredState.EXPORT_DB:
|
|
343
|
+
if self.device not in [ReplState.C, ReplState.X]:
|
|
344
|
+
return (False, None)
|
|
345
|
+
|
|
346
|
+
if not self.export_session:
|
|
347
|
+
def error():
|
|
348
|
+
if self.in_repl:
|
|
349
|
+
log2("Select an export database first with 'use' command.")
|
|
350
|
+
else:
|
|
351
|
+
log2('* export database is missing.')
|
|
352
|
+
log2()
|
|
353
|
+
display_help()
|
|
354
|
+
return (False, error)
|
|
355
|
+
|
|
356
|
+
elif required in [ReplState.L, ReplState.A, ReplState.C, ReplState.P, ReplState.X] and self.device != required:
|
|
357
|
+
def error():
|
|
358
|
+
if self.in_repl:
|
|
359
|
+
log2(f'Switch to {required}: first.')
|
|
360
|
+
else:
|
|
361
|
+
log2('* on a wrong device.')
|
|
362
|
+
log2()
|
|
363
|
+
display_help()
|
|
364
|
+
return (False, error)
|
|
365
|
+
|
|
366
|
+
return (True, None)
|
|
367
|
+
|
|
368
|
+
def _validate_device(self, devices: list[RequiredState]):
|
|
369
|
+
if self.device not in devices:
|
|
370
|
+
def error():
|
|
371
|
+
if self.in_repl:
|
|
372
|
+
log2(f'Not a valid command on {self.device}: drive.')
|
|
373
|
+
else:
|
|
374
|
+
log2('* on a wrong device.')
|
|
375
|
+
log2()
|
|
376
|
+
display_help()
|
|
377
|
+
return (False, error)
|
|
378
|
+
|
|
379
|
+
return (True, None)
|
|
380
|
+
|
|
381
|
+
def user_pass(self, secret_path = 'cql.secret'):
|
|
382
|
+
return Secrets.get_user_pass(self.pod if self.pod else self.sts, self.namespace, secret_path=secret_path)
|
|
383
|
+
|
|
384
|
+
def enter_bash(self, bash_session: BashSession):
|
|
385
|
+
self.push()
|
|
386
|
+
|
|
387
|
+
self.bash_session = bash_session
|
|
388
|
+
|
|
389
|
+
def exit_bash(self):
|
|
390
|
+
self.pop()
|
|
391
|
+
self.bash_session = None
|
|
392
|
+
|
|
393
|
+
def push(self):
|
|
394
|
+
if not self.original_state:
|
|
395
|
+
self.original_state = copy(self)
|
|
396
|
+
|
|
397
|
+
def pop(self):
|
|
398
|
+
if o := self.original_state:
|
|
399
|
+
self.device = o.device
|
|
400
|
+
self.sts = o.sts
|
|
401
|
+
self.pod = o.pod
|
|
402
|
+
self.pg_path = o.pg_path
|
|
403
|
+
self.app_env = o.app_env
|
|
404
|
+
self.app_app = o.app_app
|
|
405
|
+
self.app_pod = o.app_pod
|
|
406
|
+
# self.export_session = o.export_session
|
|
407
|
+
self.namespace = o.namespace
|
|
408
|
+
|
|
409
|
+
self.original_state = None
|
|
410
|
+
|
|
411
|
+
class DevicePodService:
|
|
412
|
+
def __init__(self, handler: 'DeviceExecHandler'):
|
|
413
|
+
self.handler = handler
|
|
414
|
+
|
|
415
|
+
def exec(self, command: str, action='bash', show_out = True):
|
|
416
|
+
state = self.handler.state
|
|
417
|
+
|
|
418
|
+
rs = None
|
|
419
|
+
if state.device == ReplState.A and state.app_app:
|
|
420
|
+
if state.app_pod:
|
|
421
|
+
rs = [AppPods.exec(state.app_pod, state.namespace, command, show_out=show_out)]
|
|
422
|
+
else:
|
|
423
|
+
pods = AppPods.pod_names(state.namespace, state.app_env, state.app_app)
|
|
424
|
+
rs = AppClusters.exec(pods, state.namespace, command, show_out=show_out)
|
|
425
|
+
elif state.pod:
|
|
426
|
+
rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=show_out)]
|
|
427
|
+
elif state.sts:
|
|
428
|
+
rs = CassandraClusters.exec(state.sts, state.namespace, command, action=action, show_out=show_out)
|
|
429
|
+
# assume that pg-agent or ops pod is single pod
|
|
430
|
+
|
|
431
|
+
dir = None
|
|
432
|
+
if rs:
|
|
433
|
+
for r in rs:
|
|
434
|
+
if r.exit_code(): # if fails to read the session file, ignore
|
|
435
|
+
continue
|
|
436
|
+
|
|
437
|
+
dir0 = r.stdout.strip(' \r\n')
|
|
438
|
+
if dir:
|
|
439
|
+
if dir != dir0:
|
|
440
|
+
log2('Inconsitent working dir found across multiple pods.')
|
|
441
|
+
return None
|
|
442
|
+
else:
|
|
443
|
+
dir = dir0
|
|
444
|
+
|
|
445
|
+
return dir
|
|
446
|
+
|
|
447
|
+
class DeviceExecHandler:
|
|
448
|
+
def __init__(self, state: ReplState):
|
|
449
|
+
self.state = state
|
|
450
|
+
|
|
451
|
+
def __enter__(self):
|
|
452
|
+
return DevicePodService(self)
|
|
453
|
+
|
|
454
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
455
|
+
return False
|
|
456
|
+
|
|
457
|
+
def device(state: ReplState):
|
|
458
|
+
return DeviceExecHandler(state)
|
adam/sql/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Callable
|
|
3
|
+
|
|
4
|
+
import sqlparse
|
|
5
|
+
from sqlparse.sql import Statement, Token
|
|
6
|
+
|
|
7
|
+
from adam.sql.term_completer import TermCompleter
|
|
8
|
+
from adam.utils_repl.automata_completer import AutomataCompleter
|
|
9
|
+
from adam.sql.sql_state_machine import AthenaStateMachine, CqlStateMachine, SqlStateMachine
|
|
10
|
+
from adam.utils_repl.state_machine import State
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"SqlCompleter",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
def default_columns(x: list[str]):
|
|
17
|
+
return 'id,x.,y.,z.'.split(',')
|
|
18
|
+
|
|
19
|
+
class SqlVariant(Enum):
|
|
20
|
+
SQL = 'sql'
|
|
21
|
+
CQL = 'cql'
|
|
22
|
+
ATHENA = 'athena'
|
|
23
|
+
|
|
24
|
+
class SqlCompleter(AutomataCompleter[Token]):
|
|
25
|
+
def tokens(self, text: str) -> list[Token]:
|
|
26
|
+
tokens = []
|
|
27
|
+
|
|
28
|
+
stmts = sqlparse.parse(text)
|
|
29
|
+
if not stmts:
|
|
30
|
+
tokens = []
|
|
31
|
+
else:
|
|
32
|
+
statement: Statement = stmts[0]
|
|
33
|
+
tokens = statement.tokens
|
|
34
|
+
|
|
35
|
+
return tokens
|
|
36
|
+
|
|
37
|
+
def __init__(self,
|
|
38
|
+
tables: Callable[[], list[str]],
|
|
39
|
+
dml: str = None,
|
|
40
|
+
expandables: dict = {},
|
|
41
|
+
variant: SqlVariant = SqlVariant.SQL,
|
|
42
|
+
debug = False):
|
|
43
|
+
machine = SqlStateMachine(debug=debug)
|
|
44
|
+
if variant == SqlVariant.CQL:
|
|
45
|
+
machine = CqlStateMachine(debug=debug)
|
|
46
|
+
elif variant == SqlVariant.ATHENA:
|
|
47
|
+
machine = AthenaStateMachine(debug=debug)
|
|
48
|
+
super().__init__(machine, dml, debug)
|
|
49
|
+
|
|
50
|
+
self.tables = tables
|
|
51
|
+
if 'columns' not in expandables:
|
|
52
|
+
expandables['columns'] = default_columns
|
|
53
|
+
self.expandables = expandables
|
|
54
|
+
self.variant = variant
|
|
55
|
+
self.debug = debug
|
|
56
|
+
|
|
57
|
+
def suggestions_completer(self, state: State, suggestions: str) -> list[str]:
|
|
58
|
+
if not suggestions:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
terms = []
|
|
62
|
+
for suggestion in suggestions.split(','):
|
|
63
|
+
terms.extend(self._terms(state, suggestion))
|
|
64
|
+
|
|
65
|
+
return TermCompleter(terms)
|
|
66
|
+
|
|
67
|
+
def _terms(self, state: State, word: str) -> list[str]:
|
|
68
|
+
terms = []
|
|
69
|
+
|
|
70
|
+
if word.startswith('`') and word.endswith('`'):
|
|
71
|
+
terms.append(word.strip('`'))
|
|
72
|
+
elif word == 'tables':
|
|
73
|
+
terms.extend(self.tables())
|
|
74
|
+
elif word == 'columns':
|
|
75
|
+
if 'last_name' in state.context and (n := state.context['last_name']):
|
|
76
|
+
if 'last_namespace' in state.context and (ns := state.context['last_namespace']):
|
|
77
|
+
n = f'{ns}.{n}'
|
|
78
|
+
terms.extend(self._call_expandable(word, [n]))
|
|
79
|
+
else:
|
|
80
|
+
terms.extend(self._call_expandable(word, []))
|
|
81
|
+
elif word == 'partition-columns':
|
|
82
|
+
terms.extend(self._call_expandable(word, []))
|
|
83
|
+
elif word == 'table-props':
|
|
84
|
+
terms.extend(self._call_expandable(word).keys())
|
|
85
|
+
elif word == 'table-prop-values':
|
|
86
|
+
if 'last_name' in state.context and state.context['last_name']:
|
|
87
|
+
table_props = self._call_expandable('table-props')
|
|
88
|
+
terms.extend(table_props[state.context['last_name']])
|
|
89
|
+
elif word == 'single':
|
|
90
|
+
terms.append("'")
|
|
91
|
+
elif word == 'comma':
|
|
92
|
+
terms.append(",")
|
|
93
|
+
elif word in self.machine.expandable_names():
|
|
94
|
+
terms.extend(self._call_expandable(word))
|
|
95
|
+
else:
|
|
96
|
+
terms.append(word)
|
|
97
|
+
|
|
98
|
+
return terms
|
|
99
|
+
|
|
100
|
+
def _call_expandable(self, name: str, *args):
|
|
101
|
+
if name in self.expandables:
|
|
102
|
+
c = self.expandables[name]
|
|
103
|
+
if args:
|
|
104
|
+
return c(args)
|
|
105
|
+
else:
|
|
106
|
+
return c()
|
|
107
|
+
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
def completions_for_nesting(self, dml: str = None):
|
|
111
|
+
if dml:
|
|
112
|
+
return {dml: SqlCompleter(self.tables, dml, expandables=self.expandables, variant=self.variant)}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
word: SqlCompleter(self.tables, word, expandables=self.expandables, variant=self.variant)
|
|
116
|
+
for word in self.machine.suggestions[''].strip(' ').split(',')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
def __str__(self):
|
|
120
|
+
return f'{self.variant}, {self.first_term}'
|