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.

Files changed (228) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -12
  3. adam/apps.py +20 -6
  4. adam/batch.py +16 -6
  5. adam/checks/check_utils.py +19 -49
  6. adam/checks/compactionstats.py +1 -1
  7. adam/checks/cpu.py +9 -3
  8. adam/checks/cpu_metrics.py +52 -0
  9. adam/checks/disk.py +3 -4
  10. adam/checks/gossip.py +1 -1
  11. adam/checks/memory.py +3 -3
  12. adam/checks/status.py +1 -1
  13. adam/columns/columns.py +3 -1
  14. adam/columns/cpu.py +3 -1
  15. adam/columns/cpu_metrics.py +22 -0
  16. adam/columns/memory.py +3 -4
  17. adam/commands/__init__.py +24 -0
  18. adam/commands/alter_tables.py +66 -0
  19. adam/commands/app/app.py +38 -0
  20. adam/commands/{app_ping.py → app/app_ping.py} +8 -14
  21. adam/commands/app/show_app_actions.py +49 -0
  22. adam/commands/{show → app}/show_app_id.py +9 -12
  23. adam/commands/{show → app}/show_app_queues.py +8 -14
  24. adam/commands/app/utils_app.py +106 -0
  25. adam/commands/audit/__init__.py +0 -0
  26. adam/commands/audit/audit.py +67 -0
  27. adam/commands/audit/audit_repair_tables.py +72 -0
  28. adam/commands/audit/audit_run.py +50 -0
  29. adam/commands/audit/completions_l.py +15 -0
  30. adam/commands/audit/show_last10.py +36 -0
  31. adam/commands/audit/show_slow10.py +36 -0
  32. adam/commands/audit/show_top10.py +36 -0
  33. adam/commands/audit/utils_show_top10.py +71 -0
  34. adam/commands/bash/__init__.py +5 -0
  35. adam/commands/bash/bash.py +36 -0
  36. adam/commands/bash/bash_completer.py +93 -0
  37. adam/commands/bash/utils_bash.py +16 -0
  38. adam/commands/cat.py +36 -0
  39. adam/commands/cd.py +14 -88
  40. adam/commands/check.py +18 -21
  41. adam/commands/cli_commands.py +11 -7
  42. adam/commands/clipboard_copy.py +87 -0
  43. adam/commands/code.py +57 -0
  44. adam/commands/command.py +220 -19
  45. adam/commands/commands_utils.py +28 -31
  46. adam/commands/cql/__init__.py +0 -0
  47. adam/commands/cql/completions_c.py +28 -0
  48. adam/commands/{cqlsh.py → cql/cqlsh.py} +13 -32
  49. adam/commands/cql/utils_cql.py +305 -0
  50. adam/commands/deploy/code_start.py +7 -10
  51. adam/commands/deploy/code_stop.py +4 -21
  52. adam/commands/deploy/code_utils.py +5 -5
  53. adam/commands/deploy/deploy.py +4 -40
  54. adam/commands/deploy/deploy_frontend.py +15 -18
  55. adam/commands/deploy/deploy_pg_agent.py +4 -7
  56. adam/commands/deploy/deploy_pod.py +74 -77
  57. adam/commands/deploy/deploy_utils.py +16 -26
  58. adam/commands/deploy/undeploy.py +4 -40
  59. adam/commands/deploy/undeploy_frontend.py +5 -8
  60. adam/commands/deploy/undeploy_pg_agent.py +7 -8
  61. adam/commands/deploy/undeploy_pod.py +16 -17
  62. adam/commands/devices/__init__.py +0 -0
  63. adam/commands/devices/device.py +149 -0
  64. adam/commands/devices/device_app.py +163 -0
  65. adam/commands/devices/device_auit_log.py +49 -0
  66. adam/commands/devices/device_cass.py +179 -0
  67. adam/commands/devices/device_export.py +87 -0
  68. adam/commands/devices/device_postgres.py +160 -0
  69. adam/commands/devices/devices.py +25 -0
  70. adam/commands/download_file.py +47 -0
  71. adam/commands/exit.py +1 -4
  72. adam/commands/export/__init__.py +0 -0
  73. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  74. adam/commands/export/clean_up_export_sessions.py +39 -0
  75. adam/commands/export/completions_x.py +11 -0
  76. adam/commands/export/download_export_session.py +40 -0
  77. adam/commands/export/drop_export_database.py +39 -0
  78. adam/commands/export/drop_export_databases.py +37 -0
  79. adam/commands/export/export.py +37 -0
  80. adam/commands/export/export_databases.py +246 -0
  81. adam/commands/export/export_select.py +34 -0
  82. adam/commands/export/export_sessions.py +209 -0
  83. adam/commands/export/export_use.py +49 -0
  84. adam/commands/export/export_x_select.py +48 -0
  85. adam/commands/export/exporter.py +332 -0
  86. adam/commands/export/import_files.py +44 -0
  87. adam/commands/export/import_session.py +44 -0
  88. adam/commands/export/importer.py +81 -0
  89. adam/commands/export/importer_athena.py +148 -0
  90. adam/commands/export/importer_sqlite.py +67 -0
  91. adam/commands/export/show_column_counts.py +45 -0
  92. adam/commands/export/show_export_databases.py +39 -0
  93. adam/commands/export/show_export_session.py +39 -0
  94. adam/commands/export/show_export_sessions.py +37 -0
  95. adam/commands/export/utils_export.py +344 -0
  96. adam/commands/find_files.py +51 -0
  97. adam/commands/find_processes.py +76 -0
  98. adam/commands/head.py +36 -0
  99. adam/commands/help.py +14 -9
  100. adam/commands/intermediate_command.py +52 -0
  101. adam/commands/issues.py +14 -40
  102. adam/commands/kubectl.py +38 -0
  103. adam/commands/login.py +26 -25
  104. adam/commands/logs.py +5 -7
  105. adam/commands/ls.py +11 -115
  106. adam/commands/medusa/medusa.py +4 -46
  107. adam/commands/medusa/medusa_backup.py +22 -29
  108. adam/commands/medusa/medusa_restore.py +51 -49
  109. adam/commands/medusa/medusa_show_backupjobs.py +20 -21
  110. adam/commands/medusa/medusa_show_restorejobs.py +16 -21
  111. adam/commands/medusa/utils_medusa.py +15 -0
  112. adam/commands/nodetool.py +8 -17
  113. adam/commands/param_get.py +11 -14
  114. adam/commands/param_set.py +9 -13
  115. adam/commands/postgres/completions_p.py +22 -0
  116. adam/commands/postgres/postgres.py +49 -73
  117. adam/commands/postgres/postgres_databases.py +270 -0
  118. adam/commands/postgres/postgres_ls.py +4 -8
  119. adam/commands/postgres/postgres_preview.py +5 -9
  120. adam/commands/postgres/utils_postgres.py +79 -0
  121. adam/commands/preview_table.py +10 -69
  122. adam/commands/pwd.py +14 -43
  123. adam/commands/reaper/reaper.py +6 -49
  124. adam/commands/reaper/reaper_forward.py +49 -56
  125. adam/commands/reaper/reaper_forward_session.py +6 -0
  126. adam/commands/reaper/reaper_forward_stop.py +10 -16
  127. adam/commands/reaper/reaper_restart.py +8 -15
  128. adam/commands/reaper/reaper_run_abort.py +8 -33
  129. adam/commands/reaper/reaper_runs.py +43 -58
  130. adam/commands/reaper/reaper_runs_abort.py +29 -49
  131. adam/commands/reaper/reaper_schedule_activate.py +14 -33
  132. adam/commands/reaper/reaper_schedule_start.py +9 -33
  133. adam/commands/reaper/reaper_schedule_stop.py +9 -33
  134. adam/commands/reaper/reaper_schedules.py +4 -14
  135. adam/commands/reaper/reaper_status.py +8 -16
  136. adam/commands/reaper/utils_reaper.py +203 -0
  137. adam/commands/repair/repair.py +4 -46
  138. adam/commands/repair/repair_log.py +6 -12
  139. adam/commands/repair/repair_run.py +29 -36
  140. adam/commands/repair/repair_scan.py +33 -41
  141. adam/commands/repair/repair_stop.py +6 -13
  142. adam/commands/report.py +25 -21
  143. adam/commands/restart.py +27 -28
  144. adam/commands/rollout.py +20 -25
  145. adam/commands/shell.py +12 -4
  146. adam/commands/show/show.py +15 -46
  147. adam/commands/show/show_adam.py +3 -3
  148. adam/commands/show/show_cassandra_repairs.py +37 -0
  149. adam/commands/show/show_cassandra_status.py +48 -52
  150. adam/commands/show/show_cassandra_version.py +5 -18
  151. adam/commands/show/show_cli_commands.py +56 -0
  152. adam/commands/show/show_host.py +33 -0
  153. adam/commands/show/show_login.py +23 -27
  154. adam/commands/show/show_params.py +2 -5
  155. adam/commands/show/show_processes.py +18 -21
  156. adam/commands/show/show_storage.py +11 -20
  157. adam/commands/watch.py +27 -30
  158. adam/config.py +8 -6
  159. adam/embedded_params.py +1 -1
  160. adam/log.py +4 -4
  161. adam/pod_exec_result.py +13 -5
  162. adam/repl.py +136 -120
  163. adam/repl_commands.py +66 -24
  164. adam/repl_session.py +8 -1
  165. adam/repl_state.py +343 -73
  166. adam/sql/__init__.py +0 -0
  167. adam/sql/lark_completer.py +284 -0
  168. adam/sql/lark_parser.py +604 -0
  169. adam/sql/sql_completer.py +118 -0
  170. adam/sql/sql_state_machine.py +630 -0
  171. adam/sql/term_completer.py +76 -0
  172. adam/sso/authn_ad.py +7 -9
  173. adam/sso/authn_okta.py +4 -6
  174. adam/sso/cred_cache.py +4 -6
  175. adam/sso/idp.py +10 -13
  176. adam/utils.py +539 -11
  177. adam/utils_athena.py +145 -0
  178. adam/utils_audits.py +102 -0
  179. adam/utils_issues.py +32 -0
  180. adam/utils_k8s/__init__.py +0 -0
  181. adam/utils_k8s/app_clusters.py +28 -0
  182. adam/utils_k8s/app_pods.py +36 -0
  183. adam/utils_k8s/cassandra_clusters.py +44 -0
  184. adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
  185. adam/{k8s_utils → utils_k8s}/custom_resources.py +16 -17
  186. adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
  187. adam/{k8s_utils → utils_k8s}/ingresses.py +2 -2
  188. adam/{k8s_utils → utils_k8s}/jobs.py +7 -11
  189. adam/utils_k8s/k8s.py +96 -0
  190. adam/{k8s_utils → utils_k8s}/kube_context.py +3 -3
  191. adam/{k8s_utils → utils_k8s}/pods.py +132 -83
  192. adam/{k8s_utils → utils_k8s}/secrets.py +7 -3
  193. adam/{k8s_utils → utils_k8s}/service_accounts.py +5 -4
  194. adam/{k8s_utils → utils_k8s}/services.py +2 -2
  195. adam/{k8s_utils → utils_k8s}/statefulsets.py +9 -16
  196. adam/utils_local.py +4 -0
  197. adam/utils_net.py +24 -0
  198. adam/utils_repl/__init__.py +0 -0
  199. adam/utils_repl/appendable_completer.py +6 -0
  200. adam/utils_repl/automata_completer.py +48 -0
  201. adam/utils_repl/repl_completer.py +172 -0
  202. adam/utils_repl/state_machine.py +173 -0
  203. adam/utils_sqlite.py +137 -0
  204. adam/version.py +1 -1
  205. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
  206. kaqing-2.0.189.dist-info/RECORD +253 -0
  207. kaqing-2.0.189.dist-info/top_level.txt +2 -0
  208. teddy/__init__.py +0 -0
  209. teddy/lark_parser.py +436 -0
  210. teddy/lark_parser2.py +618 -0
  211. adam/commands/app.py +0 -67
  212. adam/commands/bash.py +0 -87
  213. adam/commands/cp.py +0 -95
  214. adam/commands/cql_utils.py +0 -53
  215. adam/commands/devices.py +0 -89
  216. adam/commands/postgres/postgres_session.py +0 -247
  217. adam/commands/reaper/reaper_session.py +0 -159
  218. adam/commands/show/show_app_actions.py +0 -53
  219. adam/commands/show/show_commands.py +0 -61
  220. adam/commands/show/show_repairs.py +0 -47
  221. adam/k8s_utils/cassandra_clusters.py +0 -48
  222. kaqing-2.0.14.dist-info/RECORD +0 -167
  223. kaqing-2.0.14.dist-info/top_level.txt +0 -1
  224. /adam/{k8s_utils → commands/app}/__init__.py +0 -0
  225. /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
  226. /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
  227. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
  228. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
adam/repl_commands.py CHANGED
@@ -1,5 +1,13 @@
1
- from adam.commands.app import App
2
- from adam.commands.app_ping import AppPing
1
+ from adam.commands.alter_tables import AlterTables
2
+ from adam.commands.app.app import App
3
+ from adam.commands.app.app_ping import AppPing
4
+ from adam.commands.app.show_app_actions import ShowAppActions
5
+ from adam.commands.app.show_app_id import ShowAppId
6
+ from adam.commands.app.show_app_queues import ShowAppQueues
7
+ from adam.commands.audit.audit import Audit
8
+ from adam.commands.cat import Cat
9
+ from adam.commands.code import Code
10
+ from adam.commands.download_file import DownloadFile
3
11
  from adam.commands.deploy.code_start import CodeStart
4
12
  from adam.commands.deploy.code_stop import CodeStop
5
13
  from adam.commands.deploy.deploy import Deploy
@@ -10,22 +18,44 @@ from adam.commands.deploy.undeploy import Undeploy
10
18
  from adam.commands.deploy.undeploy_frontend import UndeployFrontend
11
19
  from adam.commands.deploy.undeploy_pg_agent import UndeployPgAgent
12
20
  from adam.commands.deploy.undeploy_pod import UndeployPod
21
+ from adam.commands.devices.device_app import DeviceApp
22
+ from adam.commands.devices.device_auit_log import DeviceAuditLog
23
+ from adam.commands.devices.device_cass import DeviceCass
24
+ from adam.commands.devices.device_export import DeviceExport
25
+ from adam.commands.devices.device_postgres import DevicePostgres
26
+ from adam.commands.export.download_export_session import DownloadExportSession
27
+ from adam.commands.export.drop_export_database import DropExportDatabase
28
+ from adam.commands.export.export import ExportTables
29
+ from adam.commands.export.import_files import ImportCSVFiles
30
+ from adam.commands.export.import_session import ImportSession
31
+ from adam.commands.export.clean_up_export_sessions import CleanUpExportSessions
32
+ from adam.commands.export.clean_up_all_export_sessions import CleanUpAllExportSessions
33
+ from adam.commands.export.drop_export_databases import DropExportDatabases
34
+ from adam.commands.export.export_x_select import ExportXSelect
35
+ from adam.commands.export.export_use import ExportUse
36
+ from adam.commands.export.export_select import ExportSelect
37
+ from adam.commands.export.show_column_counts import ShowColumnCounts
38
+ from adam.commands.export.show_export_databases import ShowExportDatabases
39
+ from adam.commands.export.show_export_session import ShowExportSession
40
+ from adam.commands.export.show_export_sessions import ShowExportSessions
41
+ from adam.commands.find_files import FindLocalFiles
42
+ from adam.commands.find_processes import FindProcesses
43
+ from adam.commands.head import Head
44
+ from adam.commands.kubectl import Kubectl
13
45
  from adam.commands.shell import Shell
14
- from adam.commands.show.show_app_queues import ShowAppQueues
15
- from adam.commands.cp import ClipboardCopy
16
- from adam.commands.bash import Bash
46
+ from adam.commands.clipboard_copy import ClipboardCopy
47
+ from adam.commands.bash.bash import Bash
17
48
  from adam.commands.cd import Cd
18
49
  from adam.commands.check import Check
19
50
  from adam.commands.command import Command
20
- from adam.commands.cqlsh import Cqlsh
21
- from adam.commands.devices import DeviceApp, DeviceCass, DevicePostgres
51
+ from adam.commands.cql.cqlsh import Cqlsh
22
52
  from adam.commands.exit import Exit
23
53
  from adam.commands.medusa.medusa import Medusa
24
54
  from adam.commands.param_get import GetParam
25
55
  from adam.commands.issues import Issues
26
56
  from adam.commands.ls import Ls
27
57
  from adam.commands.nodetool import NodeTool
28
- from adam.commands.postgres.postgres import Postgres
58
+ from adam.commands.postgres.postgres import Postgres, PostgresPg
29
59
  from adam.commands.preview_table import PreviewTable
30
60
  from adam.commands.pwd import Pwd
31
61
  from adam.commands.reaper.reaper import Reaper
@@ -35,25 +65,24 @@ from adam.commands.restart import Restart
35
65
  from adam.commands.rollout import RollOut
36
66
  from adam.commands.param_set import SetParam
37
67
  from adam.commands.show.show import Show
38
- from adam.commands.show.show_app_actions import ShowAppActions
39
- from adam.commands.show.show_app_id import ShowAppId
40
68
  from adam.commands.show.show_cassandra_status import ShowCassandraStatus
41
69
  from adam.commands.show.show_cassandra_version import ShowCassandraVersion
42
- from adam.commands.show.show_commands import ShowKubectlCommands
70
+ from adam.commands.show.show_cli_commands import ShowKubectlCommands
71
+ from adam.commands.show.show_host import ShowHost
43
72
  from adam.commands.show.show_login import ShowLogin
44
73
  from adam.commands.show.show_params import ShowParams
45
74
  from adam.commands.show.show_processes import ShowProcesses
46
- from adam.commands.show.show_repairs import ShowRepairs
75
+ from adam.commands.show.show_cassandra_repairs import ShowCassandraRepairs
47
76
  from adam.commands.show.show_storage import ShowStorage
48
77
  from adam.commands.show.show_adam import ShowAdam
49
78
  from adam.commands.watch import Watch
50
79
 
51
80
  class ReplCommands:
52
81
  def repl_cmd_list() -> list[Command]:
53
- cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_check() + ReplCommands.cassandra_ops() + \
54
- ReplCommands.tools() + ReplCommands.app() + ReplCommands.exit()
82
+ cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_ops() + ReplCommands.postgres_ops() + \
83
+ ReplCommands.app_ops() + ReplCommands.audit_ops() + ReplCommands.export_ops() + ReplCommands.tools() + ReplCommands.exit()
55
84
 
56
- intermediate_cmds: list[Command] = [App(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
85
+ intermediate_cmds: list[Command] = [App(), Audit(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
57
86
  ic = [c.command() for c in intermediate_cmds]
58
87
  # 1. dedup commands
59
88
  deduped = []
@@ -70,20 +99,33 @@ class ReplCommands:
70
99
  return deduped
71
100
 
72
101
  def navigation() -> list[Command]:
73
- return [Ls(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), Cd(), Pwd(), ClipboardCopy(),
74
- GetParam(), SetParam(), ShowParams(), ShowKubectlCommands(), ShowLogin(), ShowAdam()]
75
-
76
- def cassandra_check() -> list[Command]:
77
- return [ShowCassandraStatus(), ShowCassandraVersion(), ShowRepairs(), ShowStorage(), ShowProcesses(), Check(), Issues(), NodeTool(), Report()]
102
+ return [Ls(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), DeviceAuditLog(), DeviceExport(),
103
+ Cd(), Cat(), Head(), DownloadFile(), FindLocalFiles(), FindProcesses(), Pwd(), ClipboardCopy(),
104
+ GetParam(), SetParam(), ShowParams(), ShowKubectlCommands(), ShowLogin(), ShowAdam(), ShowHost()]
78
105
 
79
106
  def cassandra_ops() -> list[Command]:
80
- return Medusa.cmd_list() + [Restart(), RollOut(), Watch()] + Reaper.cmd_list() + Repair.cmd_list()
107
+ return [Cqlsh(), ShowCassandraStatus(), ShowCassandraVersion(), ShowCassandraRepairs(), ShowStorage(), ShowProcesses(),
108
+ Check(), Issues(), NodeTool(), Report(), AlterTables(), Bash(),
109
+ ExportTables(), ExportXSelect(), ExportUse(), ShowExportDatabases(), ShowColumnCounts(),
110
+ DropExportDatabase(), DropExportDatabases(),
111
+ ShowExportSessions(), ShowExportSession(), DownloadExportSession(),
112
+ CleanUpExportSessions(), CleanUpAllExportSessions(), ImportSession(), ImportCSVFiles()] + \
113
+ Medusa().cmd_list() + [Restart(), RollOut(), Watch()] + Reaper().cmd_list() + Repair().cmd_list()
81
114
 
82
- def tools() -> list[Command]:
83
- return [Cqlsh(), Postgres(), Bash(), Shell(), CodeStart(), CodeStop(), DeployFrontend(), UndeployFrontend(), DeployPod(), UndeployPod(), DeployPgAgent(), UndeployPgAgent()]
115
+ def postgres_ops() -> list[Command]:
116
+ return [Postgres(), DeployPgAgent(), UndeployPgAgent(), PostgresPg()]
84
117
 
85
- def app() -> list[Command]:
118
+ def app_ops() -> list[Command]:
86
119
  return [ShowAppActions(), ShowAppId(), ShowAppQueues(), AppPing(), App()]
87
120
 
121
+ def audit_ops() -> list[Command]:
122
+ return [Audit()] + Audit().cmd_list()
123
+
124
+ def export_ops() -> list[Command]:
125
+ return [ExportSelect(), DropExportDatabase(), DropExportDatabases(), ShowColumnCounts()]
126
+
127
+ def tools() -> list[Command]:
128
+ return [Shell(), CodeStart(), CodeStop(), DeployFrontend(), UndeployFrontend(), DeployPod(), UndeployPod(), Kubectl(), Code()]
129
+
88
130
  def exit() -> list[Command]:
89
131
  return [Exit()]
adam/repl_session.py CHANGED
@@ -1,6 +1,8 @@
1
1
  from prompt_toolkit import PromptSession
2
2
  from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
3
3
 
4
+ from adam.config import Config
5
+
4
6
  class ReplSession:
5
7
  # the singleton pattern
6
8
  def __new__(cls, *args, **kwargs):
@@ -10,4 +12,9 @@ class ReplSession:
10
12
 
11
13
  def __init__(self):
12
14
  if not hasattr(self, 'prompt_session'):
13
- self.prompt_session = PromptSession(auto_suggest=AutoSuggestFromHistory())
15
+ self.prompt_session = PromptSession(auto_suggest=AutoSuggestFromHistory())
16
+
17
+ def append_history(self, entry: str):
18
+ if entry and Config().get('repl.history.push-cat-remote-log-file', True):
19
+ if self.prompt_session:
20
+ self.prompt_session.history.append_string(entry)
adam/repl_state.py CHANGED
@@ -1,11 +1,14 @@
1
- import copy
1
+ from copy import copy
2
2
  from enum import Enum
3
-
4
- from adam.commands.postgres.postgres_session import PostgresSession
5
- from adam.k8s_utils.cassandra_clusters import CassandraClusters
6
- from adam.k8s_utils.cassandra_nodes import CassandraNodes
7
- from adam.k8s_utils.kube_context import KubeContext
8
- from adam.k8s_utils.secrets import Secrets
3
+ import re
4
+ from typing import Callable
5
+
6
+ from adam.utils_k8s.app_clusters import AppClusters
7
+ from adam.utils_k8s.app_pods import AppPods
8
+ from adam.utils_k8s.cassandra_clusters import CassandraClusters
9
+ from adam.utils_k8s.cassandra_nodes import CassandraNodes
10
+ from adam.utils_k8s.kube_context import KubeContext
11
+ from adam.utils_k8s.secrets import Secrets
9
12
  from adam.utils import display_help, log2, random_alphanumeric
10
13
 
11
14
  class BashSession:
@@ -16,25 +19,8 @@ class BashSession:
16
19
  def pwd(self, state: 'ReplState'):
17
20
  command = f'cat /tmp/.qing-{self.session_id}'
18
21
 
19
- if state.pod:
20
- rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=False)]
21
- elif state.sts:
22
- rs = CassandraClusters.exec(state.sts, state.namespace, command, action='bash', show_out=False)
23
-
24
- dir = None
25
- for r in rs:
26
- if r.exit_code(): # if fails to read the session file, ignore
27
- continue
28
-
29
- dir0 = r.stdout.strip(' \r\n')
30
- if dir:
31
- if dir != dir0:
32
- log2('Inconsitent working dir found across multiple pods.')
33
- return None
34
- else:
35
- dir = dir0
36
-
37
- return dir
22
+ with device(state) as pods:
23
+ return pods.exec(command, action='bash', show_out=False)
38
24
 
39
25
  class RequiredState(Enum):
40
26
  CLUSTER = 'cluster'
@@ -43,16 +29,22 @@ class RequiredState(Enum):
43
29
  NAMESPACE = 'namespace'
44
30
  PG_DATABASE = 'pg_database'
45
31
  APP_APP = 'app_app'
32
+ EXPORT_DB = 'export_db'
46
33
 
47
34
  class ReplState:
48
35
  A = 'a'
49
36
  C = 'c'
37
+ L = 'l'
50
38
  P = 'p'
39
+ X = 'x'
40
+
41
+ ANY = [A, C, L, P, X]
42
+ NON_L = [A, C, P, X]
51
43
 
52
44
  def __init__(self, device: str = None,
53
45
  sts: str = None, pod: str = None, namespace: str = None, ns_sts: str = None,
54
46
  pg_path: str = None,
55
- app_env: str = None, app_app: str = None,
47
+ app_env: str = None, app_app: str = None, app_pod: str = None,
56
48
  in_repl = False, bash_session: BashSession = None, remote_dir = None):
57
49
  self.namespace = KubeContext.in_cluster_namespace()
58
50
 
@@ -62,12 +54,16 @@ class ReplState:
62
54
  self.pg_path = pg_path
63
55
  self.app_env = app_env
64
56
  self.app_app = app_app
57
+ self.app_pod = app_pod
65
58
  if namespace:
66
59
  self.namespace = namespace
67
60
  self.in_repl = in_repl
68
61
  self.bash_session = bash_session
69
62
  self.remote_dir = remote_dir
70
- self.wait_log_flag = False
63
+ self.original_state: ReplState = None
64
+ self.pod_targetted = False
65
+
66
+ self.export_session: str = None
71
67
 
72
68
  if ns_sts:
73
69
  nn = ns_sts.split('@')
@@ -82,13 +78,51 @@ class ReplState:
82
78
  def __hash__(self):
83
79
  return hash((self.sts, self.pod))
84
80
 
85
- def apply_args(self, args: list[str], cmd: list[str] = None, resolve_pg = True) -> tuple['ReplState', list[str]]:
81
+ def __str__(self):
82
+ msg = ''
83
+ if self.device == ReplState.P:
84
+ msg = f'{ReplState.P}:'
85
+ host, database = self.pg_host_n_database()
86
+ if database:
87
+ msg += database
88
+ elif host:
89
+ msg += 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]]:
86
120
  state = self
87
121
 
88
122
  new_args = []
89
123
  for index, arg in enumerate(args):
90
- if index < 6:
91
- state = copy.copy(state)
124
+ if index < args_to_check:
125
+ state = copy(state)
92
126
 
93
127
  s, n = KubeContext.is_sts_name(arg)
94
128
  if s:
@@ -120,92 +154,328 @@ class ReplState:
120
154
 
121
155
  return (state, new_args)
122
156
 
123
- def validate(self, required: RequiredState = None, pg_required: RequiredState = None, app_required: RequiredState = None):
124
- if not pg_required and not app_required:
125
- if required == RequiredState.CLUSTER:
126
- if not self.namespace or not self.sts:
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():
127
263
  if self.in_repl:
128
264
  log2('cd to a Cassandra cluster first.')
129
265
  else:
130
266
  log2('* Cassandra cluster is missing.')
131
267
  log2()
132
268
  display_help()
269
+ return (False, error)
270
+
271
+ elif required == RequiredState.POD:
272
+ if self.device != ReplState.C:
273
+ return (False, None)
133
274
 
134
- return False
135
- elif required == RequiredState.POD:
136
- if not self.namespace or not self.pod:
275
+ if not self.namespace or not self.pod:
276
+ def error():
137
277
  if self.in_repl:
138
278
  log2('cd to a pod first.')
139
279
  else:
140
280
  log2('* Pod is missing.')
141
281
  log2()
142
282
  display_help()
283
+ return (False, error)
143
284
 
144
- return False
145
- elif required == RequiredState.CLUSTER_OR_POD:
146
- if not self.namespace or not self.sts and not self.pod:
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():
147
291
  if self.in_repl:
148
292
  log2('cd to a Cassandra cluster first.')
149
293
  else:
150
294
  log2('* Cassandra cluster or pod is missing.')
151
295
  log2()
152
296
  display_help()
297
+ return (False, error)
298
+
299
+ elif required == RequiredState.NAMESPACE:
300
+ if self.device != ReplState.C:
301
+ return (False, None)
153
302
 
154
- return False
155
- elif required == RequiredState.NAMESPACE:
156
- if not self.namespace:
303
+ if not self.namespace:
304
+ def error():
157
305
  if self.in_repl:
158
306
  log2('Namespace is required.')
159
307
  else:
160
308
  log2('* namespace is missing.')
161
309
  log2()
162
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
+ _, database = self.pg_host_n_database()
318
+ if not database:
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)
163
327
 
164
- return False
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
+ if self.device == ReplState.C:
350
+ log2("Select an export database first with 'use' command.")
351
+ else:
352
+ log2('cd to an export database first.')
353
+ else:
354
+ log2('* export database is missing.')
355
+ log2()
356
+ display_help()
357
+ return (False, error)
165
358
 
166
- if pg_required == RequiredState.PG_DATABASE:
167
- pg = PostgresSession(self.namespace, self.pg_path)
168
- if not pg.db:
359
+ elif required in [ReplState.L, ReplState.A, ReplState.C, ReplState.P, ReplState.X] and self.device != required:
360
+ def error():
169
361
  if self.in_repl:
170
- log2('cd to a database first.')
362
+ log2(f'Switch to {required}: first.')
171
363
  else:
172
- log2('* database is missing.')
364
+ log2('* on a wrong device.')
173
365
  log2()
174
366
  display_help()
367
+ return (False, error)
175
368
 
176
- return False
369
+ return (True, None)
177
370
 
178
- if app_required == RequiredState.APP_APP and not self.app_app:
179
- if self.in_repl:
180
- log2('cd to an app first.')
181
- else:
182
- log2('* app is missing.')
183
- log2()
184
- display_help()
185
-
186
- return False
371
+ def _validate_device(self, devices: list[RequiredState]):
372
+ if self.device not in devices:
373
+ def error():
374
+ if self.in_repl:
375
+ log2(f'Not a valid command on {self.device}: drive.')
376
+ else:
377
+ log2('* on a wrong device.')
378
+ log2()
379
+ display_help()
380
+ return (False, error)
187
381
 
188
- return True
382
+ return (True, None)
189
383
 
190
384
  def user_pass(self, secret_path = 'cql.secret'):
191
385
  return Secrets.get_user_pass(self.pod if self.pod else self.sts, self.namespace, secret_path=secret_path)
192
386
 
193
387
  def enter_bash(self, bash_session: BashSession):
388
+ self.push()
389
+
194
390
  self.bash_session = bash_session
195
- if self.device != ReplState.C:
196
- self.device = ReplState.C
197
- log2(f'Moved to {ReplState.C}: automatically. Will move back to {ReplState.P}: when you exit the bash session.')
198
391
 
199
392
  def exit_bash(self):
200
- if self.bash_session and self.bash_session.device:
201
- self.device = self.bash_session.device
202
-
393
+ self.pop()
203
394
  self.bash_session = None
204
395
 
205
- def wait_log(self, msg: str):
206
- if not self.wait_log_flag:
207
- log2(msg)
208
- self.wait_log_flag = True
396
+ def push(self, pod_targetted=False):
397
+ if not self.original_state:
398
+ self.original_state = copy(self)
399
+ self.pod_targetted = pod_targetted
400
+
401
+ def pop(self):
402
+ if o := self.original_state:
403
+ self.device = o.device
404
+ self.sts = o.sts
405
+ self.pod = o.pod
406
+ self.pg_path = o.pg_path
407
+ self.app_env = o.app_env
408
+ self.app_app = o.app_app
409
+ self.app_pod = o.app_pod
410
+ # self.export_session = o.export_session
411
+ self.namespace = o.namespace
412
+
413
+ self.original_state = None
414
+ self.pod_targetted = False
415
+
416
+ def pg_host_n_database(self):
417
+ host = None
418
+ database = None
419
+
420
+ if self.pg_path:
421
+ host_n_db = self.pg_path.split('/')
422
+ host = host_n_db[0]
423
+ if len(host_n_db) > 1:
424
+ database = host_n_db[1]
425
+
426
+ return host, database
427
+
428
+ def with_no_pod(self):
429
+ state1 = copy(self)
430
+ state1.pod = None
431
+
432
+ return state1
433
+
434
+ class DevicePodService:
435
+ def __init__(self, handler: 'DeviceExecHandler'):
436
+ self.handler = handler
437
+
438
+ def exec(self, command: str, action='bash', show_out = True):
439
+ state = self.handler.state
440
+
441
+ rs = None
442
+ if state.device == ReplState.A and state.app_app:
443
+ if state.app_pod:
444
+ rs = [AppPods.exec(state.app_pod, state.namespace, command, show_out=show_out)]
445
+ else:
446
+ pods = AppPods.pod_names(state.namespace, state.app_env, state.app_app)
447
+ rs = AppClusters.exec(pods, state.namespace, command, show_out=show_out)
448
+ elif state.pod:
449
+ rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=show_out)]
450
+ elif state.sts:
451
+ rs = CassandraClusters.exec(state.sts, state.namespace, command, action=action, show_out=show_out)
452
+ # assume that pg-agent or ops pod is single pod
453
+
454
+ dir = None
455
+ if rs:
456
+ for r in rs:
457
+ if r.exit_code(): # if fails to read the session file, ignore
458
+ continue
459
+
460
+ dir0 = r.stdout.strip(' \r\n')
461
+ if dir:
462
+ if dir != dir0:
463
+ log2('Inconsitent working dir found across multiple pods.')
464
+ return None
465
+ else:
466
+ dir = dir0
467
+
468
+ return dir
469
+
470
+ class DeviceExecHandler:
471
+ def __init__(self, state: ReplState):
472
+ self.state = state
473
+
474
+ def __enter__(self):
475
+ return DevicePodService(self)
476
+
477
+ def __exit__(self, exc_type, exc_val, exc_tb):
478
+ return False
209
479
 
210
- def clear_wait_log_flag(self):
211
- self.wait_log_flag = False
480
+ def device(state: ReplState):
481
+ return DeviceExecHandler(state)
adam/sql/__init__.py ADDED
File without changes