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
@@ -0,0 +1,274 @@
1
+ from datetime import datetime
2
+ import functools
3
+ import re
4
+ import subprocess
5
+
6
+ from adam.config import Config
7
+ from adam.repl_session import ReplSession
8
+ from adam.utils_k8s.kube_context import KubeContext
9
+ from adam.utils_k8s.pods import Pods
10
+ from adam.utils_k8s.secrets import Secrets
11
+ from adam.utils import log2
12
+
13
+ class PostgresContext:
14
+ def apply(namespace: str, path: str, arg: str = None) -> 'PostgresContext':
15
+ context = PostgresContext(namespace, path)
16
+
17
+ if arg:
18
+ if arg == '..':
19
+ if context.db:
20
+ context.db = None
21
+ else:
22
+ context.host = None
23
+ else:
24
+ tks = arg.split('@')
25
+ if not context.host:
26
+ context.host = tks[0]
27
+ else:
28
+ context.db = tks[0]
29
+
30
+ if not namespace and tks[1]:
31
+ context.namespace = tks[1]
32
+
33
+ return context
34
+
35
+ def __init__(self, ns: str, path: str):
36
+ self.namespace = ns
37
+ self.conn_details = None
38
+ self.host = None
39
+ self.db = None
40
+
41
+ if path:
42
+ tks = path.split('/')
43
+ hn = tks[0].split('@')
44
+ self.host = hn[0]
45
+ if len(hn) > 1 and not ns:
46
+ self.namespace = hn[1]
47
+
48
+ if len(tks) > 1:
49
+ self.db = tks[1]
50
+
51
+ def path(self):
52
+ if not self.host:
53
+ return None
54
+
55
+ d = self.host
56
+ if not self.db:
57
+ return d
58
+
59
+ return f'{self.host}/{self.db}'
60
+
61
+ def hosts(ns: str):
62
+ return PostgresContext.hosts_for_namespace(ns)
63
+
64
+ @functools.lru_cache()
65
+ def hosts_for_namespace(ns: str):
66
+ ss = Secrets.list_secrets(ns, name_pattern=Config().get('pg.name-pattern', '^{namespace}.*k8spg.*'))
67
+
68
+ def excludes(name: str):
69
+ exs = Config().get('pg.excludes', '.helm., -admin-secret')
70
+ if exs:
71
+ for ex in exs.split(','):
72
+ if ex.strip(' ') in name:
73
+ return True
74
+
75
+ return False
76
+
77
+ return [s for s in ss if not excludes(s)]
78
+
79
+ def databases(self):
80
+ dbs = []
81
+ # List of databases
82
+ # Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
83
+ # ---------------------------------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
84
+ # postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
85
+ # stgawsscpsr_c3_c3 | postgres | UTF8 | C | C | | libc |
86
+ # template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
87
+ # | | | | | | | postgres=CTc/postgres
88
+ # (48 rows)
89
+ if r := self.run_sql('\l', show_out=False):
90
+ s = 0
91
+ for line in r.stdout.split('\n'):
92
+ line: str = line.strip(' \r')
93
+ if s == 0:
94
+ if 'List of databases' in line:
95
+ s = 1
96
+ elif s == 1:
97
+ if 'Name' in line and 'Owner' in line and 'Encoding' in line:
98
+ s = 2
99
+ elif s == 2:
100
+ if line.startswith('---------'):
101
+ s = 3
102
+ elif s == 3:
103
+ groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
104
+ if groups and groups[1] != '|':
105
+ dbs.append({'name': groups[1], 'owner': groups[2]})
106
+
107
+ return dbs
108
+
109
+ def tables(self):
110
+ dbs = []
111
+ # List of relations
112
+ # Schema | Name | Type | Owner
113
+ # ----------+------------------------------------------------------------+-------+---------------
114
+ # postgres | c3_2_admin_aclpriv | table | postgres
115
+ # postgres | c3_2_admin_aclpriv_a | table | postgres
116
+ if r := self.run_sql('\dt', show_out=False):
117
+ s = 0
118
+ for line in r.stdout.split('\n'):
119
+ line: str = line.strip(' \r')
120
+ if s == 0:
121
+ if 'List of relations' in line:
122
+ s = 1
123
+ elif s == 1:
124
+ if 'Schema' in line and 'Name' in line and 'Type' in line:
125
+ s = 2
126
+ elif s == 2:
127
+ if line.startswith('---------'):
128
+ s = 3
129
+ elif s == 3:
130
+ groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
131
+ if groups and groups[1] != '|':
132
+ dbs.append({'schema': groups[1], 'name': groups[2]})
133
+
134
+ return dbs
135
+
136
+ def run_sql(self, sql: str, show_out = True, background = False):
137
+ db = self.db if self.db else PostgresContext.default_db()
138
+
139
+ if KubeContext.in_cluster():
140
+ cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
141
+ log2(f'{cmd1} "{sql}"')
142
+ # remove double quotes from the sql argument
143
+ cmd = cmd1.split(' ') + [sql]
144
+
145
+ r = subprocess.run(cmd, capture_output=not background, text=True)
146
+ if show_out:
147
+ log2(r.stdout)
148
+ log2(r.stderr)
149
+
150
+ return r
151
+ else:
152
+ pod_name, container_name = PostgresContext.pod_and_container(self.namespace)
153
+ if not pod_name:
154
+ return
155
+
156
+ # ns = self.namespace
157
+ # pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
158
+
159
+ # if Config().get('pg.agent.just-in-time', False):
160
+ # if not PostgresContext.deploy_pg_agent(pod_name, ns):
161
+ # return
162
+
163
+ # real_pod_name = pod_name
164
+ # try:
165
+ # # try with dedicated pg agent pod name configured
166
+ # Pods.get(ns, pod_name)
167
+ # except:
168
+ # try:
169
+ # # try with the ops pod
170
+ # pod_name = Config().get('pod.name', 'ops')
171
+ # real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
172
+ # except:
173
+ # log2(f"Could not locate {pod_name} pod.")
174
+ # return None
175
+
176
+ cmd = f'psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
177
+ env_prefix = f'PGPASSWORD="{self.password()}"'
178
+
179
+ r = Pods.exec(pod_name, container_name, self.namespace, cmd, show_out=show_out, background=background, env_prefix=env_prefix)
180
+ if r and Config().get('repl.history.push-cat-remote-log-file', True):
181
+ if r.log_file and ReplSession().prompt_session:
182
+ ReplSession().prompt_session.history.append_string(f'@{r.pod} cat {r.log_file}')
183
+
184
+ return r
185
+
186
+ def pod_and_container(ns: str):
187
+ container_name = Config().get('pg.agent.name', 'ops-pg-agent')
188
+
189
+ if Config().get('pg.agent.just-in-time', False):
190
+ if not PostgresContext.deploy_pg_agent(container_name, ns):
191
+ return None
192
+
193
+ pod_name = container_name
194
+ try:
195
+ # try with dedicated pg agent pod name configured
196
+ Pods.get(ns, container_name)
197
+ except:
198
+ try:
199
+ # try with the ops pod
200
+ container_name = Config().get('pod.name', 'ops')
201
+ pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
202
+ except:
203
+ log2(f"Could not locate {container_name} pod.")
204
+ return None
205
+
206
+ return pod_name, container_name
207
+
208
+ def deploy_pg_agent(pod_name: str, ns: str) -> str:
209
+ image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
210
+ timeout = Config().get('pg.agent.timeout', 3600)
211
+ try:
212
+ Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
213
+ except Exception as e:
214
+ if e.status == 409:
215
+ if Pods.completed(ns, pod_name):
216
+ try:
217
+ Pods.delete(pod_name, ns)
218
+ Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
219
+ except Exception as e2:
220
+ log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
221
+
222
+ return
223
+ else:
224
+ log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
225
+
226
+ return
227
+
228
+ Pods.wait_for_running(ns, pod_name)
229
+
230
+ return pod_name
231
+
232
+ def undeploy_pg_agent(pod_name: str, ns: str):
233
+ Pods.delete(pod_name, ns, grace_period_seconds=0)
234
+
235
+ def endpoint(self):
236
+ if not self.conn_details:
237
+ self.conn_details = Secrets.get_data(self.namespace, self.host)
238
+
239
+ endpoint_key = Config().get('pg.secret.endpoint-key', 'postgres-db-endpoint')
240
+
241
+ return self.conn_details[endpoint_key] if endpoint_key in self.conn_details else ''
242
+
243
+ def port(self):
244
+ if not self.conn_details:
245
+ self.conn_details = Secrets.get_data(self.namespace, self.host)
246
+
247
+ port_key = Config().get('pg.secret.port-key', 'postgres-db-port')
248
+
249
+ return self.conn_details[port_key] if port_key in self.conn_details else ''
250
+
251
+ def username(self):
252
+ if not self.conn_details:
253
+ self.conn_details = Secrets.get_data(self.namespace, self.host)
254
+
255
+ username_key = Config().get('pg.secret.username-key', 'postgres-admin-username')
256
+
257
+ return self.conn_details[username_key] if username_key in self.conn_details else ''
258
+
259
+ def password(self):
260
+ if not self.conn_details:
261
+ self.conn_details = Secrets.get_data(self.namespace, self.host)
262
+
263
+ password_key = Config().get('pg.secret.password-key', 'postgres-admin-password')
264
+
265
+ return self.conn_details[password_key] if password_key in self.conn_details else ''
266
+
267
+ def default_db():
268
+ return Config().get('pg.default-db', 'postgres')
269
+
270
+ def default_owner():
271
+ return Config().get('pg.default-owner', 'postgres')
272
+
273
+ def default_schema():
274
+ return Config().get('pg.default-schema', 'postgres')
@@ -1,10 +1,9 @@
1
- from walker.commands.command import Command
2
- from walker.commands.ls import Ls
3
- from walker.repl_state import ReplState, RequiredState
1
+ from adam.commands.command import Command
2
+ from adam.commands.ls import Ls
3
+ from adam.repl_state import ReplState, RequiredState
4
4
 
5
5
  class PostgresLs(Command):
6
6
  COMMAND = 'pg ls'
7
- reaper_login = None
8
7
 
9
8
  # the singleton pattern
10
9
  def __new__(cls, *args, **kwargs):
@@ -25,15 +24,12 @@ class PostgresLs(Command):
25
24
  if not(args := self.args(cmd)):
26
25
  return super().run(cmd, state)
27
26
 
28
- state, args = self.apply_state(args, state)
29
- if not self.validate_state(state):
30
- return state
31
-
32
- state.device = ReplState.P
27
+ with self.validate(args, state) as (args, state):
28
+ state.device = ReplState.P
33
29
 
34
- Ls().run('ls', state)
30
+ Ls().run('ls', state)
35
31
 
36
- return state
32
+ return state
37
33
 
38
34
  def completion(self, state: ReplState):
39
35
  if state.sts:
@@ -1,11 +1,9 @@
1
- from walker.commands.command import Command
2
- from walker.commands.ls import Ls
3
- from walker.commands.preview_table import PreviewTable
4
- from walker.repl_state import ReplState, RequiredState
1
+ from adam.commands.command import Command
2
+ from adam.commands.preview_table import PreviewTable
3
+ from adam.repl_state import ReplState, RequiredState
5
4
 
6
5
  class PostgresPreview(Command):
7
6
  COMMAND = 'pg preview'
8
- reaper_login = None
9
7
 
10
8
  # the singleton pattern
11
9
  def __new__(cls, *args, **kwargs):
@@ -20,21 +18,18 @@ class PostgresPreview(Command):
20
18
  return PostgresPreview.COMMAND
21
19
 
22
20
  def required(self):
23
- return RequiredState.NAMESPACE
21
+ return RequiredState.PG_DATABASE
24
22
 
25
23
  def run(self, cmd: str, state: ReplState):
26
24
  if not(args := self.args(cmd)):
27
25
  return super().run(cmd, state)
28
26
 
29
- state, args = self.apply_state(args, state)
30
- if not self.validate_state(state, RequiredState.PG_DATABASE):
31
- return state
32
-
33
- state.device = ReplState.P
27
+ with self.validate(args, state) as (args, state):
28
+ state.device = ReplState.P
34
29
 
35
- PreviewTable().run(f'preview {" ".join(args)}', state)
30
+ PreviewTable().run(f'preview {" ".join(args)}', state)
36
31
 
37
- return state
32
+ return state
38
33
 
39
34
  def completion(self, state: ReplState):
40
35
  if state.sts:
@@ -0,0 +1,10 @@
1
+ from adam.commands.postgres.utils_postgres import pg_table_names
2
+ from adam.sql.sql_completer import SqlCompleter
3
+
4
+ def psql_completions(ns: str, pg_path: str):
5
+ return {
6
+ '\h': None,
7
+ '\d': None,
8
+ '\dt': None,
9
+ '\du': None
10
+ } | SqlCompleter(lambda: pg_table_names(ns, pg_path)).completions_for_nesting()
@@ -0,0 +1,66 @@
1
+ import functools
2
+
3
+ from adam.commands.postgres.postgres_context import PostgresContext
4
+ from adam.repl_state import ReplState
5
+ from adam.utils import log2, wait_log
6
+ from adam.utils_k8s.pods import Pods
7
+
8
+ TestPG = [False]
9
+
10
+ @functools.lru_cache()
11
+ def pg_database_names(ns: str, pg_path: str):
12
+ if TestPG[0]:
13
+ return ['azops88_c3ai_c3']
14
+
15
+ wait_log('Inspecting Postgres Databases...')
16
+
17
+ pg = PostgresContext.apply(ns, pg_path)
18
+ return [db['name'] for db in pg.databases() if db['owner'] == PostgresContext.default_owner()]
19
+
20
+ @functools.lru_cache()
21
+ def pg_table_names(ns: str, pg_path: str):
22
+ if TestPG[0]:
23
+ return ['C3_2_XYZ1']
24
+
25
+ wait_log('Inspecting Postgres Database...')
26
+ return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresContext.default_schema()]
27
+
28
+ def pg_tables(ns: str, pg_path: str):
29
+ pg = PostgresContext.apply(ns, pg_path)
30
+ if pg.db:
31
+ return pg.tables()
32
+
33
+ return []
34
+
35
+ class PostgresPodService:
36
+ def __init__(self, handler: 'PostgresExecHandler'):
37
+ self.handler = handler
38
+
39
+ def exec(self, command: str, show_out=True):
40
+ state = self.handler.state
41
+
42
+ pod, container = PostgresContext.pod_and_container(state.namespace)
43
+ if not pod:
44
+ log2('Cannot locate postgres agent or ops pod.')
45
+ return state
46
+
47
+ return Pods.exec(pod, container, state.namespace, command, show_out=show_out)
48
+
49
+ def sql(self, args: list[str], background=False):
50
+ state = self.handler.state
51
+
52
+ PostgresContext.apply(state.namespace, state.pg_path).run_sql(' '.join(args), background=background)
53
+
54
+ class PostgresExecHandler:
55
+ def __init__(self, state: ReplState, background=False):
56
+ self.state = state
57
+ self.background = background
58
+
59
+ def __enter__(self):
60
+ return PostgresPodService(self)
61
+
62
+ def __exit__(self, exc_type, exc_val, exc_tb):
63
+ return False
64
+
65
+ def postgres(state: ReplState, background=False):
66
+ return PostgresExecHandler(state, background=background)
@@ -0,0 +1,37 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.devices.devices import Devices
3
+ from adam.repl_state import ReplState, RequiredState
4
+
5
+ class PreviewTable(Command):
6
+ COMMAND = 'preview'
7
+
8
+ # the singleton pattern
9
+ def __new__(cls, *args, **kwargs):
10
+ if not hasattr(cls, 'instance'): cls.instance = super(PreviewTable, cls).__new__(cls)
11
+
12
+ return cls.instance
13
+
14
+ def __init__(self, successor: Command=None):
15
+ super().__init__(successor)
16
+
17
+ def command(self):
18
+ return PreviewTable.COMMAND
19
+
20
+ def required(self):
21
+ return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L]
22
+
23
+ def run(self, cmd: str, state: ReplState):
24
+ if not(args := self.args(cmd)):
25
+ return super().run(cmd, state)
26
+
27
+ with self.validate(args, state) as (args, state):
28
+ Devices.device(state).preview(args[0] if args else None, state)
29
+
30
+ return state
31
+
32
+ def completion(self, _: ReplState):
33
+ # taken care of by the sql completer
34
+ return {}
35
+
36
+ def help(self, _: ReplState):
37
+ return f'{PreviewTable.COMMAND} TABLE\t preview table'
adam/commands/pwd.py ADDED
@@ -0,0 +1,47 @@
1
+ from adam.app_session import AppSession
2
+ from adam.commands.command import Command
3
+ from adam.commands.devices.devices import Devices
4
+ from adam.repl_state import ReplState
5
+ from adam.utils import lines_to_tabular, log
6
+
7
+ class Pwd(Command):
8
+ COMMAND = 'pwd'
9
+
10
+ # the singleton pattern
11
+ def __new__(cls, *args, **kwargs):
12
+ if not hasattr(cls, 'instance'): cls.instance = super(Pwd, cls).__new__(cls)
13
+
14
+ return cls.instance
15
+
16
+ def __init__(self, successor: Command=None):
17
+ super().__init__(successor)
18
+
19
+ def command(self):
20
+ return Pwd.COMMAND
21
+
22
+ def run(self, cmd: str, state: ReplState):
23
+ if not(args := self.args(cmd)):
24
+ return super().run(cmd, state)
25
+
26
+ with self.validate(args, state) as (_, state):
27
+ host = "unknown"
28
+ try:
29
+ app_session: AppSession = AppSession.create('c3', 'c3')
30
+ host = app_session.host
31
+ except:
32
+ pass
33
+
34
+ log(lines_to_tabular([device.pwd(state) for device in Devices.all()] + [
35
+ f'',
36
+ f'HOST\t{host}',
37
+ f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
38
+ ], 'DEVICE\tLOCATION', separator='\t'))
39
+ log()
40
+
41
+ return state
42
+
43
+ def completion(self, state: ReplState):
44
+ return super().completion(state)
45
+
46
+ def help(self, _: ReplState):
47
+ return f'{Pwd.COMMAND}\t print current working directories'
@@ -0,0 +1,35 @@
1
+ import click
2
+
3
+ from adam.commands.intermediate_command import IntermediateCommand
4
+ from .reaper_forward import ReaperForward
5
+ from .reaper_forward_stop import ReaperForwardStop
6
+ from .reaper_restart import ReaperRestart
7
+ from .reaper_run_abort import ReaperRunAbort
8
+ from .reaper_runs import ReaperRuns
9
+ from .reaper_runs_abort import ReaperRunsAbort
10
+ from .reaper_schedule_activate import ReaperScheduleActivate
11
+ from .reaper_schedule_start import ReaperScheduleStart
12
+ from .reaper_schedule_stop import ReaperScheduleStop
13
+ from .reaper_schedules import ReaperSchedules
14
+ from .reaper_status import ReaperStatus
15
+
16
+ class Reaper(IntermediateCommand):
17
+ COMMAND = 'reaper'
18
+
19
+ # the singleton pattern
20
+ def __new__(cls, *args, **kwargs):
21
+ if not hasattr(cls, 'instance'): cls.instance = super(Reaper, cls).__new__(cls)
22
+
23
+ return cls.instance
24
+
25
+ def command(self):
26
+ return Reaper.COMMAND
27
+
28
+ def cmd_list(self):
29
+ return [ReaperSchedules(), ReaperScheduleStop(), ReaperScheduleActivate(), ReaperScheduleStart(),
30
+ ReaperForwardStop(), ReaperForward(), ReaperRunAbort(), ReaperRunsAbort(), ReaperRestart(),
31
+ ReaperRuns(), ReaperStatus()]
32
+
33
+ class ReaperCommandHelper(click.Command):
34
+ def get_help(self, ctx: click.Context):
35
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper().cmd_list(), show_cluster_help=True)
@@ -0,0 +1,93 @@
1
+ from functools import partial
2
+ import threading
3
+ import time
4
+
5
+ from adam.commands.command import Command
6
+ from adam.commands.reaper.reaper_forward_session import ReaperForwardSession
7
+ from adam.commands.reaper.utils_reaper import Reapers, port_forwarding
8
+ from adam.config import Config
9
+ from adam.repl_session import ReplSession
10
+ from adam.repl_state import ReplState, RequiredState
11
+ from adam.utils import lines_to_tabular, log2
12
+
13
+ class ReaperForward(Command):
14
+ COMMAND = 'reaper forward'
15
+
16
+ # the singleton pattern
17
+ def __new__(cls, *args, **kwargs):
18
+ if not hasattr(cls, 'instance'): cls.instance = super(ReaperForward, cls).__new__(cls)
19
+
20
+ return cls.instance
21
+
22
+ def __init__(self, successor: Command=None):
23
+ super().__init__(successor)
24
+
25
+ def command(self):
26
+ return ReaperForward.COMMAND
27
+
28
+ def required(self):
29
+ return RequiredState.CLUSTER
30
+
31
+ def run(self, cmd: str, state: ReplState):
32
+ if not(args := self.args(cmd)):
33
+ return super().run(cmd, state)
34
+
35
+ with self.validate(args, state) as (args, state):
36
+ if not Reapers.pod_name(state):
37
+ return state
38
+
39
+ spec = Reapers.reaper_spec(state)
40
+ if state.in_repl:
41
+ if ReaperForwardSession.is_forwarding:
42
+ log2("Another port-forward is already running.")
43
+
44
+ return "already-running"
45
+
46
+ # make it a daemon to exit with a Ctrl-D
47
+ thread = threading.Thread(target=self.loop, args=(state,), daemon=True)
48
+ thread.start()
49
+
50
+ while not ReaperForwardSession.is_forwarding:
51
+ time.sleep(1)
52
+
53
+ d = {
54
+ 'reaper-ui': spec["web-uri"],
55
+ 'reaper-username': spec["username"],
56
+ 'reaper-password': spec["password"]
57
+ }
58
+ log2()
59
+ log2(lines_to_tabular([f'{k},{v}' for k, v in d.items()], separator=','))
60
+
61
+ for k, v in d.items():
62
+ ReplSession().prompt_session.history.append_string(f'cp {k}')
63
+ log2()
64
+ log2(f'Use <Up> arrow key to copy the values to clipboard.')
65
+ else:
66
+ try:
67
+ log2(f'Click: {spec["web-uri"]}')
68
+ log2(f'username: {spec["username"]}')
69
+ log2(f'password: {spec["password"]}')
70
+ log2()
71
+ log2(f"Press Ctrl+C to break.")
72
+
73
+ time.sleep(Config().get('reaper.port-forward.timeout', 3600 * 24))
74
+ except KeyboardInterrupt:
75
+ pass
76
+
77
+ return state
78
+
79
+ def loop(self, state: ReplState):
80
+ with port_forwarding(state, Reapers.local_port(), partial(Reapers.svc_or_pod, state), Reapers.target_port()):
81
+ ReaperForwardSession.is_forwarding = True
82
+ try:
83
+ while not ReaperForwardSession.stopping.is_set():
84
+ time.sleep(1)
85
+ finally:
86
+ ReaperForwardSession.stopping.clear()
87
+ ReaperForwardSession.is_forwarding = False
88
+
89
+ def completion(self, state: ReplState):
90
+ return super().completion(state)
91
+
92
+ def help(self, _: ReplState):
93
+ return f'{ReaperForward.COMMAND}\t port-forward to reaper'
@@ -0,0 +1,6 @@
1
+ import threading
2
+
3
+ class ReaperForwardSession:
4
+ is_forwarding = False
5
+ stopping = threading.Event()
6
+ schedules_ids_by_cluster: dict[str, list[str]] = {}