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.
Files changed (308) hide show
  1. adam/__init__.py +1 -0
  2. adam/app_session.py +182 -0
  3. {walker → adam}/apps.py +8 -24
  4. {walker → adam}/batch.py +54 -97
  5. {walker → adam}/checks/check.py +3 -3
  6. {walker → adam}/checks/check_result.py +1 -1
  7. adam/checks/check_utils.py +65 -0
  8. {walker → adam}/checks/compactionstats.py +6 -6
  9. {walker → adam}/checks/cpu.py +14 -8
  10. adam/checks/cpu_metrics.py +52 -0
  11. {walker → adam}/checks/disk.py +6 -6
  12. {walker → adam}/checks/gossip.py +5 -5
  13. {walker → adam}/checks/memory.py +7 -7
  14. {walker → adam}/checks/status.py +5 -5
  15. {walker → adam}/cli.py +3 -3
  16. {walker → adam}/columns/column.py +1 -1
  17. adam/columns/columns.py +45 -0
  18. {walker → adam}/columns/compactions.py +5 -5
  19. {walker → adam}/columns/cpu.py +6 -4
  20. adam/columns/cpu_metrics.py +22 -0
  21. {walker → adam}/columns/dir_data.py +3 -3
  22. {walker → adam}/columns/dir_snapshots.py +3 -3
  23. {walker → adam}/columns/gossip.py +5 -5
  24. {walker → adam}/columns/host_id.py +3 -3
  25. {walker → adam}/columns/memory.py +3 -3
  26. {walker → adam}/columns/node_address.py +3 -3
  27. {walker → adam}/columns/node_load.py +3 -3
  28. {walker → adam}/columns/node_owns.py +3 -3
  29. {walker → adam}/columns/node_status.py +3 -3
  30. {walker → adam}/columns/node_tokens.py +3 -3
  31. {walker → adam}/columns/node_utils.py +2 -2
  32. {walker → adam}/columns/pod_name.py +2 -2
  33. {walker → adam}/columns/volume_cassandra.py +4 -4
  34. {walker → adam}/columns/volume_root.py +3 -3
  35. adam/commands/__init__.py +15 -0
  36. adam/commands/alter_tables.py +81 -0
  37. adam/commands/app_cmd.py +38 -0
  38. {walker → adam}/commands/app_ping.py +10 -16
  39. adam/commands/audit/audit.py +84 -0
  40. adam/commands/audit/audit_repair_tables.py +74 -0
  41. adam/commands/audit/audit_run.py +50 -0
  42. adam/commands/audit/show_last10.py +48 -0
  43. adam/commands/audit/show_slow10.py +47 -0
  44. adam/commands/audit/show_top10.py +45 -0
  45. adam/commands/audit/utils_show_top10.py +59 -0
  46. adam/commands/bash/__init__.py +5 -0
  47. adam/commands/bash/bash.py +36 -0
  48. adam/commands/bash/bash_completer.py +93 -0
  49. adam/commands/bash/utils_bash.py +16 -0
  50. adam/commands/cat.py +50 -0
  51. adam/commands/cd.py +43 -0
  52. adam/commands/check.py +73 -0
  53. {walker → adam}/commands/cli_commands.py +7 -8
  54. adam/commands/code.py +57 -0
  55. adam/commands/command.py +190 -0
  56. {walker → adam}/commands/command_helpers.py +1 -1
  57. {walker → adam}/commands/commands_utils.py +15 -25
  58. adam/commands/cp.py +89 -0
  59. adam/commands/cql/cql_completions.py +33 -0
  60. {walker/commands → adam/commands/cql}/cqlsh.py +20 -35
  61. adam/commands/cql/utils_cql.py +343 -0
  62. {walker/commands/frontend → adam/commands/deploy}/code_start.py +11 -14
  63. adam/commands/deploy/code_stop.py +40 -0
  64. {walker/commands/frontend → adam/commands/deploy}/code_utils.py +7 -9
  65. adam/commands/deploy/deploy.py +25 -0
  66. adam/commands/deploy/deploy_frontend.py +49 -0
  67. adam/commands/deploy/deploy_pg_agent.py +35 -0
  68. adam/commands/deploy/deploy_pod.py +108 -0
  69. adam/commands/deploy/deploy_utils.py +29 -0
  70. adam/commands/deploy/undeploy.py +25 -0
  71. adam/commands/deploy/undeploy_frontend.py +38 -0
  72. adam/commands/deploy/undeploy_pg_agent.py +39 -0
  73. adam/commands/deploy/undeploy_pod.py +48 -0
  74. adam/commands/devices/device.py +118 -0
  75. adam/commands/devices/device_app.py +173 -0
  76. adam/commands/devices/device_auit_log.py +49 -0
  77. adam/commands/devices/device_cass.py +185 -0
  78. adam/commands/devices/device_export.py +86 -0
  79. adam/commands/devices/device_postgres.py +144 -0
  80. adam/commands/devices/devices.py +25 -0
  81. {walker → adam}/commands/exit.py +3 -6
  82. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  83. adam/commands/export/clean_up_export_sessions.py +51 -0
  84. adam/commands/export/drop_export_database.py +55 -0
  85. adam/commands/export/drop_export_databases.py +43 -0
  86. adam/commands/export/export.py +53 -0
  87. adam/commands/export/export_databases.py +170 -0
  88. adam/commands/export/export_handlers.py +71 -0
  89. adam/commands/export/export_select.py +81 -0
  90. adam/commands/export/export_select_x.py +54 -0
  91. adam/commands/export/export_use.py +52 -0
  92. adam/commands/export/exporter.py +352 -0
  93. adam/commands/export/import_session.py +40 -0
  94. adam/commands/export/importer.py +67 -0
  95. adam/commands/export/importer_athena.py +80 -0
  96. adam/commands/export/importer_sqlite.py +47 -0
  97. adam/commands/export/show_column_counts.py +54 -0
  98. adam/commands/export/show_export_databases.py +36 -0
  99. adam/commands/export/show_export_session.py +48 -0
  100. adam/commands/export/show_export_sessions.py +44 -0
  101. adam/commands/export/utils_export.py +314 -0
  102. {walker → adam}/commands/help.py +17 -12
  103. adam/commands/intermediate_command.py +49 -0
  104. adam/commands/issues.py +43 -0
  105. adam/commands/kubectl.py +38 -0
  106. adam/commands/login.py +70 -0
  107. {walker → adam}/commands/logs.py +8 -10
  108. adam/commands/ls.py +41 -0
  109. adam/commands/medusa/medusa.py +27 -0
  110. adam/commands/medusa/medusa_backup.py +57 -0
  111. adam/commands/medusa/medusa_restore.py +83 -0
  112. adam/commands/medusa/medusa_show_backupjobs.py +51 -0
  113. adam/commands/medusa/medusa_show_restorejobs.py +47 -0
  114. {walker → adam}/commands/nodetool.py +17 -21
  115. {walker → adam}/commands/param_get.py +15 -16
  116. adam/commands/param_set.py +43 -0
  117. adam/commands/postgres/postgres.py +104 -0
  118. adam/commands/postgres/postgres_context.py +274 -0
  119. {walker → adam}/commands/postgres/postgres_ls.py +7 -11
  120. {walker → adam}/commands/postgres/postgres_preview.py +8 -13
  121. adam/commands/postgres/psql_completions.py +10 -0
  122. adam/commands/postgres/utils_postgres.py +66 -0
  123. adam/commands/preview_table.py +37 -0
  124. adam/commands/pwd.py +47 -0
  125. adam/commands/reaper/reaper.py +35 -0
  126. adam/commands/reaper/reaper_forward.py +93 -0
  127. adam/commands/reaper/reaper_forward_session.py +6 -0
  128. {walker → adam}/commands/reaper/reaper_forward_stop.py +13 -19
  129. {walker → adam}/commands/reaper/reaper_restart.py +10 -17
  130. adam/commands/reaper/reaper_run_abort.py +46 -0
  131. adam/commands/reaper/reaper_runs.py +82 -0
  132. adam/commands/reaper/reaper_runs_abort.py +63 -0
  133. adam/commands/reaper/reaper_schedule_activate.py +45 -0
  134. adam/commands/reaper/reaper_schedule_start.py +45 -0
  135. adam/commands/reaper/reaper_schedule_stop.py +45 -0
  136. {walker → adam}/commands/reaper/reaper_schedules.py +6 -16
  137. {walker → adam}/commands/reaper/reaper_status.py +11 -19
  138. adam/commands/reaper/utils_reaper.py +196 -0
  139. adam/commands/repair/repair.py +26 -0
  140. {walker → adam}/commands/repair/repair_log.py +7 -10
  141. adam/commands/repair/repair_run.py +70 -0
  142. adam/commands/repair/repair_scan.py +71 -0
  143. {walker → adam}/commands/repair/repair_stop.py +8 -11
  144. adam/commands/report.py +61 -0
  145. adam/commands/restart.py +60 -0
  146. {walker → adam}/commands/rollout.py +25 -30
  147. adam/commands/shell.py +34 -0
  148. adam/commands/show/show.py +39 -0
  149. walker/commands/show/show_version.py → adam/commands/show/show_adam.py +14 -10
  150. adam/commands/show/show_app_actions.py +57 -0
  151. {walker → adam}/commands/show/show_app_id.py +12 -15
  152. {walker → adam}/commands/show/show_app_queues.py +9 -12
  153. adam/commands/show/show_cassandra_repairs.py +38 -0
  154. adam/commands/show/show_cassandra_status.py +124 -0
  155. {walker → adam}/commands/show/show_cassandra_version.py +6 -16
  156. adam/commands/show/show_commands.py +59 -0
  157. walker/commands/show/show_storage.py → adam/commands/show/show_host.py +11 -13
  158. adam/commands/show/show_login.py +62 -0
  159. {walker → adam}/commands/show/show_params.py +4 -4
  160. adam/commands/show/show_processes.py +51 -0
  161. adam/commands/show/show_storage.py +42 -0
  162. adam/commands/watch.py +82 -0
  163. {walker → adam}/config.py +10 -22
  164. {walker → adam}/embedded_apps.py +1 -1
  165. adam/embedded_params.py +2 -0
  166. adam/log.py +47 -0
  167. {walker → adam}/pod_exec_result.py +10 -2
  168. adam/repl.py +182 -0
  169. adam/repl_commands.py +124 -0
  170. adam/repl_state.py +458 -0
  171. adam/sql/__init__.py +0 -0
  172. adam/sql/sql_completer.py +120 -0
  173. adam/sql/sql_state_machine.py +618 -0
  174. adam/sql/term_completer.py +76 -0
  175. adam/sso/__init__.py +0 -0
  176. {walker → adam}/sso/authenticator.py +5 -1
  177. adam/sso/authn_ad.py +170 -0
  178. {walker → adam}/sso/authn_okta.py +39 -22
  179. adam/sso/cred_cache.py +60 -0
  180. adam/sso/id_token.py +23 -0
  181. adam/sso/idp.py +143 -0
  182. adam/sso/idp_login.py +50 -0
  183. adam/sso/idp_session.py +55 -0
  184. adam/sso/sso_config.py +63 -0
  185. adam/utils.py +679 -0
  186. adam/utils_app.py +98 -0
  187. adam/utils_athena.py +145 -0
  188. adam/utils_audits.py +106 -0
  189. adam/utils_issues.py +32 -0
  190. adam/utils_k8s/__init__.py +0 -0
  191. adam/utils_k8s/app_clusters.py +28 -0
  192. adam/utils_k8s/app_pods.py +33 -0
  193. adam/utils_k8s/cassandra_clusters.py +36 -0
  194. adam/utils_k8s/cassandra_nodes.py +33 -0
  195. adam/utils_k8s/config_maps.py +34 -0
  196. {walker/k8s_utils → adam/utils_k8s}/custom_resources.py +7 -2
  197. adam/utils_k8s/deployment.py +56 -0
  198. {walker/k8s_utils → adam/utils_k8s}/ingresses.py +3 -4
  199. {walker/k8s_utils → adam/utils_k8s}/jobs.py +3 -3
  200. adam/utils_k8s/k8s.py +87 -0
  201. {walker/k8s_utils → adam/utils_k8s}/kube_context.py +4 -4
  202. adam/utils_k8s/pods.py +290 -0
  203. {walker/k8s_utils → adam/utils_k8s}/secrets.py +8 -4
  204. adam/utils_k8s/service_accounts.py +170 -0
  205. {walker/k8s_utils → adam/utils_k8s}/services.py +3 -4
  206. {walker/k8s_utils → adam/utils_k8s}/statefulsets.py +6 -16
  207. {walker/k8s_utils → adam/utils_k8s}/volumes.py +10 -1
  208. adam/utils_net.py +24 -0
  209. adam/utils_repl/__init__.py +0 -0
  210. adam/utils_repl/automata_completer.py +48 -0
  211. adam/utils_repl/repl_completer.py +46 -0
  212. adam/utils_repl/state_machine.py +173 -0
  213. adam/utils_sqlite.py +109 -0
  214. adam/version.py +5 -0
  215. {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/METADATA +1 -1
  216. kaqing-2.0.171.dist-info/RECORD +236 -0
  217. kaqing-2.0.171.dist-info/entry_points.txt +3 -0
  218. kaqing-2.0.171.dist-info/top_level.txt +1 -0
  219. kaqing-1.77.0.dist-info/RECORD +0 -159
  220. kaqing-1.77.0.dist-info/entry_points.txt +0 -3
  221. kaqing-1.77.0.dist-info/top_level.txt +0 -1
  222. walker/__init__.py +0 -3
  223. walker/app_session.py +0 -168
  224. walker/checks/check_utils.py +0 -97
  225. walker/columns/columns.py +0 -43
  226. walker/commands/add_user.py +0 -68
  227. walker/commands/app.py +0 -67
  228. walker/commands/bash.py +0 -87
  229. walker/commands/cd.py +0 -115
  230. walker/commands/check.py +0 -68
  231. walker/commands/command.py +0 -104
  232. walker/commands/cp.py +0 -95
  233. walker/commands/cql_utils.py +0 -53
  234. walker/commands/devices.py +0 -89
  235. walker/commands/frontend/code_stop.py +0 -57
  236. walker/commands/frontend/setup.py +0 -60
  237. walker/commands/frontend/setup_frontend.py +0 -58
  238. walker/commands/frontend/teardown.py +0 -61
  239. walker/commands/frontend/teardown_frontend.py +0 -42
  240. walker/commands/issues.py +0 -69
  241. walker/commands/login.py +0 -72
  242. walker/commands/ls.py +0 -145
  243. walker/commands/medusa/medusa.py +0 -69
  244. walker/commands/medusa/medusa_backup.py +0 -61
  245. walker/commands/medusa/medusa_restore.py +0 -86
  246. walker/commands/medusa/medusa_show_backupjobs.py +0 -52
  247. walker/commands/medusa/medusa_show_restorejobs.py +0 -52
  248. walker/commands/param_set.py +0 -44
  249. walker/commands/postgres/postgres.py +0 -113
  250. walker/commands/postgres/postgres_session.py +0 -225
  251. walker/commands/preview_table.py +0 -98
  252. walker/commands/processes.py +0 -53
  253. walker/commands/pwd.py +0 -64
  254. walker/commands/reaper/reaper.py +0 -78
  255. walker/commands/reaper/reaper_forward.py +0 -100
  256. walker/commands/reaper/reaper_run_abort.py +0 -65
  257. walker/commands/reaper/reaper_runs.py +0 -97
  258. walker/commands/reaper/reaper_runs_abort.py +0 -83
  259. walker/commands/reaper/reaper_schedule_activate.py +0 -64
  260. walker/commands/reaper/reaper_schedule_start.py +0 -64
  261. walker/commands/reaper/reaper_schedule_stop.py +0 -64
  262. walker/commands/reaper/reaper_session.py +0 -159
  263. walker/commands/repair/repair.py +0 -68
  264. walker/commands/repair/repair_run.py +0 -72
  265. walker/commands/repair/repair_scan.py +0 -79
  266. walker/commands/report.py +0 -57
  267. walker/commands/restart.py +0 -61
  268. walker/commands/show/show.py +0 -72
  269. walker/commands/show/show_app_actions.py +0 -53
  270. walker/commands/show/show_cassandra_status.py +0 -35
  271. walker/commands/show/show_commands.py +0 -58
  272. walker/commands/show/show_processes.py +0 -35
  273. walker/commands/show/show_repairs.py +0 -47
  274. walker/commands/status.py +0 -128
  275. walker/commands/storage.py +0 -52
  276. walker/commands/user_entry.py +0 -69
  277. walker/commands/watch.py +0 -85
  278. walker/embedded_params.py +0 -2
  279. walker/k8s_utils/cassandra_clusters.py +0 -48
  280. walker/k8s_utils/cassandra_nodes.py +0 -26
  281. walker/k8s_utils/pods.py +0 -211
  282. walker/repl.py +0 -165
  283. walker/repl_commands.py +0 -58
  284. walker/repl_state.py +0 -211
  285. walker/sso/authn_ad.py +0 -94
  286. walker/sso/idp.py +0 -150
  287. walker/sso/idp_login.py +0 -29
  288. walker/sso/sso_config.py +0 -45
  289. walker/utils.py +0 -194
  290. walker/version.py +0 -5
  291. {walker → adam}/checks/__init__.py +0 -0
  292. {walker → adam}/checks/check_context.py +0 -0
  293. {walker → adam}/checks/issue.py +0 -0
  294. {walker → adam}/cli_group.py +0 -0
  295. {walker → adam}/columns/__init__.py +0 -0
  296. {walker/commands → adam/commands/audit}/__init__.py +0 -0
  297. {walker/commands/frontend → adam/commands/cql}/__init__.py +0 -0
  298. {walker/commands/medusa → adam/commands/deploy}/__init__.py +0 -0
  299. {walker/commands/postgres → adam/commands/devices}/__init__.py +0 -0
  300. {walker/commands/reaper → adam/commands/export}/__init__.py +0 -0
  301. {walker/commands/repair → adam/commands/medusa}/__init__.py +0 -0
  302. {walker → adam}/commands/nodetool_commands.py +0 -0
  303. {walker/commands/show → adam/commands/postgres}/__init__.py +0 -0
  304. {walker/k8s_utils → adam/commands/reaper}/__init__.py +0 -0
  305. {walker/sso → adam/commands/repair}/__init__.py +0 -0
  306. /walker/medusa_show_restorejobs.py → /adam/commands/show/__init__.py +0 -0
  307. {walker → adam}/repl_session.py +0 -0
  308. {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}'