kaqing 2.0.110__py3-none-any.whl → 2.0.214__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 (251) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -12
  3. adam/apps.py +18 -4
  4. adam/batch.py +19 -19
  5. adam/checks/check_utils.py +16 -46
  6. adam/checks/cpu.py +7 -1
  7. adam/checks/cpu_metrics.py +52 -0
  8. adam/checks/disk.py +2 -3
  9. adam/columns/columns.py +3 -1
  10. adam/columns/cpu.py +3 -1
  11. adam/columns/cpu_metrics.py +22 -0
  12. adam/columns/memory.py +3 -4
  13. adam/commands/__init__.py +24 -0
  14. adam/commands/app/app.py +38 -0
  15. adam/commands/{app_ping.py → app/app_ping.py} +7 -13
  16. adam/commands/{login.py → app/login.py} +22 -24
  17. adam/commands/app/show_app_actions.py +49 -0
  18. adam/commands/{show → app}/show_app_id.py +8 -11
  19. adam/commands/{show → app}/show_app_queues.py +7 -14
  20. adam/commands/app/show_login.py +56 -0
  21. adam/commands/app/utils_app.py +106 -0
  22. adam/commands/audit/audit.py +22 -40
  23. adam/commands/audit/audit_repair_tables.py +15 -19
  24. adam/commands/audit/audit_run.py +15 -22
  25. adam/commands/audit/completions_l.py +15 -0
  26. adam/commands/audit/show_last10.py +4 -18
  27. adam/commands/audit/show_slow10.py +4 -17
  28. adam/commands/audit/show_top10.py +4 -16
  29. adam/commands/audit/utils_show_top10.py +15 -3
  30. adam/commands/bash/__init__.py +5 -0
  31. adam/commands/bash/bash.py +36 -0
  32. adam/commands/bash/bash_completer.py +93 -0
  33. adam/commands/bash/utils_bash.py +16 -0
  34. adam/commands/cassandra/__init__.py +0 -0
  35. adam/commands/cassandra/download_cassandra_log.py +45 -0
  36. adam/commands/{restart.py → cassandra/restart_cluster.py} +12 -26
  37. adam/commands/cassandra/restart_node.py +51 -0
  38. adam/commands/cassandra/restart_nodes.py +47 -0
  39. adam/commands/{rollout.py → cassandra/rollout.py} +20 -25
  40. adam/commands/cassandra/show_cassandra_repairs.py +37 -0
  41. adam/commands/cassandra/show_cassandra_status.py +117 -0
  42. adam/commands/{show → cassandra}/show_cassandra_version.py +5 -18
  43. adam/commands/cassandra/show_processes.py +50 -0
  44. adam/commands/cassandra/show_storage.py +44 -0
  45. adam/commands/{watch.py → cassandra/watch.py} +26 -29
  46. adam/commands/cli/__init__.py +0 -0
  47. adam/commands/{cli_commands.py → cli/cli_commands.py} +8 -4
  48. adam/commands/cli/clipboard_copy.py +86 -0
  49. adam/commands/cli/show_cli_commands.py +56 -0
  50. adam/commands/code.py +57 -0
  51. adam/commands/command.py +211 -40
  52. adam/commands/commands_utils.py +20 -27
  53. adam/commands/config/__init__.py +0 -0
  54. adam/commands/{param_get.py → config/param_get.py} +11 -14
  55. adam/commands/{param_set.py → config/param_set.py} +8 -12
  56. adam/commands/{show → config}/show_params.py +2 -5
  57. adam/commands/cql/alter_tables.py +66 -0
  58. adam/commands/cql/completions_c.py +29 -0
  59. adam/commands/cql/cqlsh.py +10 -32
  60. adam/commands/cql/utils_cql.py +306 -0
  61. adam/commands/debug/__init__.py +0 -0
  62. adam/commands/debug/debug.py +22 -0
  63. adam/commands/debug/debug_completes.py +35 -0
  64. adam/commands/debug/debug_timings.py +35 -0
  65. adam/commands/debug/show_offloaded_completes.py +45 -0
  66. adam/commands/deploy/code_start.py +7 -10
  67. adam/commands/deploy/code_stop.py +4 -21
  68. adam/commands/deploy/code_utils.py +3 -3
  69. adam/commands/deploy/deploy.py +4 -27
  70. adam/commands/deploy/deploy_frontend.py +14 -17
  71. adam/commands/deploy/deploy_pg_agent.py +3 -6
  72. adam/commands/deploy/deploy_pod.py +65 -73
  73. adam/commands/deploy/deploy_utils.py +14 -24
  74. adam/commands/deploy/undeploy.py +4 -27
  75. adam/commands/deploy/undeploy_frontend.py +4 -7
  76. adam/commands/deploy/undeploy_pg_agent.py +6 -8
  77. adam/commands/deploy/undeploy_pod.py +11 -12
  78. adam/commands/devices/__init__.py +0 -0
  79. adam/commands/devices/device.py +149 -0
  80. adam/commands/devices/device_app.py +163 -0
  81. adam/commands/devices/device_auit_log.py +49 -0
  82. adam/commands/devices/device_cass.py +179 -0
  83. adam/commands/devices/device_export.py +87 -0
  84. adam/commands/devices/device_postgres.py +160 -0
  85. adam/commands/devices/devices.py +25 -0
  86. adam/commands/diag/__init__.py +0 -0
  87. adam/commands/{check.py → diag/check.py} +16 -25
  88. adam/commands/diag/generate_report.py +52 -0
  89. adam/commands/diag/issues.py +43 -0
  90. adam/commands/exit.py +1 -4
  91. adam/commands/export/__init__.py +0 -0
  92. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  93. adam/commands/export/clean_up_export_sessions.py +39 -0
  94. adam/commands/export/completions_x.py +11 -0
  95. adam/commands/export/download_export_session.py +40 -0
  96. adam/commands/export/drop_export_database.py +39 -0
  97. adam/commands/export/drop_export_databases.py +37 -0
  98. adam/commands/export/export.py +37 -0
  99. adam/commands/export/export_databases.py +251 -0
  100. adam/commands/export/export_select.py +34 -0
  101. adam/commands/export/export_sessions.py +210 -0
  102. adam/commands/export/export_use.py +49 -0
  103. adam/commands/export/export_x_select.py +48 -0
  104. adam/commands/export/exporter.py +419 -0
  105. adam/commands/export/import_files.py +44 -0
  106. adam/commands/export/import_session.py +40 -0
  107. adam/commands/export/importer.py +81 -0
  108. adam/commands/export/importer_athena.py +157 -0
  109. adam/commands/export/importer_sqlite.py +78 -0
  110. adam/commands/export/show_column_counts.py +45 -0
  111. adam/commands/export/show_export_databases.py +39 -0
  112. adam/commands/export/show_export_session.py +39 -0
  113. adam/commands/export/show_export_sessions.py +37 -0
  114. adam/commands/export/utils_export.py +366 -0
  115. adam/commands/fs/__init__.py +0 -0
  116. adam/commands/fs/cat.py +36 -0
  117. adam/commands/fs/cat_local.py +42 -0
  118. adam/commands/fs/cd.py +41 -0
  119. adam/commands/fs/download_file.py +47 -0
  120. adam/commands/fs/find_files.py +51 -0
  121. adam/commands/fs/find_processes.py +76 -0
  122. adam/commands/fs/head.py +36 -0
  123. adam/commands/fs/ls.py +41 -0
  124. adam/commands/fs/ls_local.py +40 -0
  125. adam/commands/fs/pwd.py +45 -0
  126. adam/commands/fs/rm.py +18 -0
  127. adam/commands/fs/rm_downloads.py +39 -0
  128. adam/commands/fs/rm_logs.py +38 -0
  129. adam/commands/{shell.py → fs/shell.py} +12 -4
  130. adam/commands/{show → fs}/show_adam.py +3 -3
  131. adam/commands/{show → fs}/show_host.py +1 -1
  132. adam/commands/help.py +5 -3
  133. adam/commands/intermediate_command.py +52 -0
  134. adam/commands/kubectl.py +38 -0
  135. adam/commands/medusa/medusa.py +4 -22
  136. adam/commands/medusa/medusa_backup.py +20 -27
  137. adam/commands/medusa/medusa_restore.py +35 -48
  138. adam/commands/medusa/medusa_show_backupjobs.py +16 -18
  139. adam/commands/medusa/medusa_show_restorejobs.py +13 -18
  140. adam/commands/medusa/utils_medusa.py +15 -0
  141. adam/commands/nodetool/__init__.py +0 -0
  142. adam/commands/{nodetool.py → nodetool/nodetool.py} +9 -20
  143. adam/commands/postgres/completions_p.py +22 -0
  144. adam/commands/postgres/postgres.py +47 -55
  145. adam/commands/postgres/postgres_databases.py +269 -0
  146. adam/commands/postgres/postgres_ls.py +5 -9
  147. adam/commands/postgres/postgres_preview.py +5 -9
  148. adam/commands/postgres/utils_postgres.py +80 -0
  149. adam/commands/preview_table.py +8 -44
  150. adam/commands/reaper/reaper.py +4 -27
  151. adam/commands/reaper/reaper_forward.py +49 -56
  152. adam/commands/reaper/reaper_forward_session.py +6 -0
  153. adam/commands/reaper/reaper_forward_stop.py +10 -16
  154. adam/commands/reaper/reaper_restart.py +7 -14
  155. adam/commands/reaper/reaper_run_abort.py +8 -33
  156. adam/commands/reaper/reaper_runs.py +43 -58
  157. adam/commands/reaper/reaper_runs_abort.py +29 -49
  158. adam/commands/reaper/reaper_schedule_activate.py +14 -33
  159. adam/commands/reaper/reaper_schedule_start.py +9 -33
  160. adam/commands/reaper/reaper_schedule_stop.py +9 -33
  161. adam/commands/reaper/reaper_schedules.py +4 -14
  162. adam/commands/reaper/reaper_status.py +8 -16
  163. adam/commands/reaper/utils_reaper.py +203 -0
  164. adam/commands/repair/repair.py +4 -22
  165. adam/commands/repair/repair_log.py +5 -11
  166. adam/commands/repair/repair_run.py +27 -34
  167. adam/commands/repair/repair_scan.py +32 -40
  168. adam/commands/repair/repair_stop.py +5 -12
  169. adam/commands/show.py +40 -0
  170. adam/config.py +5 -15
  171. adam/embedded_params.py +1 -1
  172. adam/log.py +4 -4
  173. adam/repl.py +83 -116
  174. adam/repl_commands.py +86 -45
  175. adam/repl_session.py +9 -1
  176. adam/repl_state.py +176 -40
  177. adam/sql/async_executor.py +62 -0
  178. adam/sql/lark_completer.py +286 -0
  179. adam/sql/lark_parser.py +604 -0
  180. adam/sql/qingl.lark +1076 -0
  181. adam/sql/sql_completer.py +52 -27
  182. adam/sql/sql_state_machine.py +131 -19
  183. adam/sso/authn_ad.py +6 -8
  184. adam/sso/authn_okta.py +4 -6
  185. adam/sso/cred_cache.py +4 -9
  186. adam/sso/idp.py +9 -12
  187. adam/utils.py +670 -31
  188. adam/utils_athena.py +145 -0
  189. adam/utils_audits.py +12 -103
  190. adam/utils_issues.py +32 -0
  191. adam/utils_k8s/app_clusters.py +35 -0
  192. adam/utils_k8s/app_pods.py +41 -0
  193. adam/utils_k8s/cassandra_clusters.py +35 -20
  194. adam/utils_k8s/cassandra_nodes.py +15 -6
  195. adam/utils_k8s/custom_resources.py +16 -17
  196. adam/utils_k8s/ingresses.py +2 -2
  197. adam/utils_k8s/jobs.py +7 -11
  198. adam/utils_k8s/k8s.py +96 -0
  199. adam/utils_k8s/kube_context.py +3 -6
  200. adam/{pod_exec_result.py → utils_k8s/pod_exec_result.py} +13 -4
  201. adam/utils_k8s/pods.py +159 -89
  202. adam/utils_k8s/secrets.py +4 -4
  203. adam/utils_k8s/service_accounts.py +5 -4
  204. adam/utils_k8s/services.py +2 -2
  205. adam/utils_k8s/statefulsets.py +6 -14
  206. adam/utils_local.py +80 -0
  207. adam/utils_net.py +4 -4
  208. adam/utils_repl/__init__.py +0 -0
  209. adam/utils_repl/appendable_completer.py +6 -0
  210. adam/utils_repl/automata_completer.py +48 -0
  211. adam/utils_repl/repl_completer.py +93 -0
  212. adam/utils_repl/state_machine.py +173 -0
  213. adam/utils_sqlite.py +132 -0
  214. adam/version.py +1 -1
  215. {kaqing-2.0.110.dist-info → kaqing-2.0.214.dist-info}/METADATA +1 -1
  216. kaqing-2.0.214.dist-info/RECORD +272 -0
  217. kaqing-2.0.214.dist-info/top_level.txt +2 -0
  218. teddy/__init__.py +0 -0
  219. teddy/lark_parser.py +436 -0
  220. teddy/lark_parser2.py +618 -0
  221. adam/commands/alter_tables.py +0 -81
  222. adam/commands/app.py +0 -67
  223. adam/commands/bash.py +0 -150
  224. adam/commands/cd.py +0 -125
  225. adam/commands/cp.py +0 -95
  226. adam/commands/cql/cql_completions.py +0 -15
  227. adam/commands/cql/cql_utils.py +0 -112
  228. adam/commands/devices.py +0 -118
  229. adam/commands/issues.py +0 -75
  230. adam/commands/logs.py +0 -40
  231. adam/commands/ls.py +0 -146
  232. adam/commands/postgres/postgres_context.py +0 -239
  233. adam/commands/postgres/postgres_utils.py +0 -31
  234. adam/commands/postgres/psql_completions.py +0 -10
  235. adam/commands/pwd.py +0 -77
  236. adam/commands/reaper/reaper_session.py +0 -159
  237. adam/commands/report.py +0 -63
  238. adam/commands/show/show.py +0 -54
  239. adam/commands/show/show_app_actions.py +0 -56
  240. adam/commands/show/show_cassandra_status.py +0 -128
  241. adam/commands/show/show_commands.py +0 -61
  242. adam/commands/show/show_login.py +0 -63
  243. adam/commands/show/show_processes.py +0 -53
  244. adam/commands/show/show_repairs.py +0 -47
  245. adam/commands/show/show_storage.py +0 -52
  246. kaqing-2.0.110.dist-info/RECORD +0 -187
  247. kaqing-2.0.110.dist-info/top_level.txt +0 -1
  248. /adam/commands/{show → app}/__init__.py +0 -0
  249. /adam/commands/{nodetool_commands.py → nodetool/nodetool_commands.py} +0 -0
  250. {kaqing-2.0.110.dist-info → kaqing-2.0.214.dist-info}/WHEEL +0 -0
  251. {kaqing-2.0.110.dist-info → kaqing-2.0.214.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,269 @@
1
+ import functools
2
+ import re
3
+ import subprocess
4
+
5
+ from adam.config import Config
6
+ from adam.repl_session import ReplSession
7
+ from adam.repl_state import ReplState
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, log_exc
12
+
13
+ class ConnectionDetails:
14
+ def __init__(self, state: ReplState, namespace: str, host: str):
15
+ self.state = state
16
+ self.namespace = namespace
17
+ self.host = host
18
+
19
+ def endpoint(self):
20
+ return PostgresDatabases._connection_property(self.state, 'pg.secret.endpoint-key', 'postgres-db-endpoint', host=self.host)
21
+
22
+ def port(self):
23
+ return PostgresDatabases._connection_property(self.state, 'pg.secret.port-key', 'postgres-db-port', host=self.host)
24
+
25
+ def username(self):
26
+ return PostgresDatabases._connection_property(self.state, 'pg.secret.username-key', 'postgres-admin-username', host=self.host)
27
+
28
+ def password(self):
29
+ return PostgresDatabases._connection_property(self.state, 'pg.secret.password-key', 'postgres-admin-password', host=self.host)
30
+
31
+ class PostgresDatabases:
32
+ def hosts(state: ReplState, namespace: str = None):
33
+ if not namespace:
34
+ namespace = state.namespace
35
+
36
+ return [ConnectionDetails(state, namespace, host) for host in PostgresDatabases.host_names(namespace)]
37
+
38
+ @functools.lru_cache()
39
+ def host_names(namespace: str):
40
+ ss = Secrets.list_secrets(namespace, name_pattern=Config().get('pg.name-pattern', '^{namespace}.*k8spg.*'))
41
+
42
+ def excludes(name: str):
43
+ exs = Config().get('pg.excludes', '.helm., -admin-secret')
44
+ if exs:
45
+ for ex in exs.split(','):
46
+ if ex.strip(' ') in name:
47
+ return True
48
+
49
+ return False
50
+
51
+ return [s for s in ss if not excludes(s)]
52
+
53
+ def databases(state: ReplState, default_owner = False):
54
+ dbs = []
55
+ # List of databases
56
+ # Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
57
+ # ---------------------------------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
58
+ # postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
59
+ # stgawsscpsr_c3_c3 | postgres | UTF8 | C | C | | libc |
60
+ # template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
61
+ # | | | | | | | postgres=CTc/postgres
62
+ # (48 rows)
63
+ if r := PostgresDatabases.run_sql(state, '\l', show_out=False):
64
+ s = 0
65
+ for line in r.stdout.split('\n'):
66
+ line: str = line.strip(' \r')
67
+ if s == 0:
68
+ if 'List of databases' in line:
69
+ s = 1
70
+ elif s == 1:
71
+ if 'Name' in line and 'Owner' in line and 'Encoding' in line:
72
+ s = 2
73
+ elif s == 2:
74
+ if line.startswith('---------'):
75
+ s = 3
76
+ elif s == 3:
77
+ groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
78
+ if groups and groups[1] != '|':
79
+ dbs.append({'name': groups[1], 'owner': groups[2]})
80
+
81
+ if default_owner:
82
+ dbs = [db for db in dbs if db['owner'] == PostgresDatabases.default_owner()]
83
+
84
+ return dbs
85
+
86
+ def tables(state: ReplState, default_schema = False):
87
+ dbs = []
88
+ # List of relations
89
+ # Schema | Name | Type | Owner
90
+ # ----------+------------------------------------------------------------+-------+---------------
91
+ # postgres | c3_2_admin_aclpriv | table | postgres
92
+ # postgres | c3_2_admin_aclpriv_a | table | postgres
93
+ if r := PostgresDatabases.run_sql(state, '\dt', show_out=False):
94
+ s = 0
95
+ for line in r.stdout.split('\n'):
96
+ line: str = line.strip(' \r')
97
+ if s == 0:
98
+ if 'List of relations' in line:
99
+ s = 1
100
+ elif s == 1:
101
+ if 'Schema' in line and 'Name' in line and 'Type' in line:
102
+ s = 2
103
+ elif s == 2:
104
+ if line.startswith('---------'):
105
+ s = 3
106
+ elif s == 3:
107
+ groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
108
+ if groups and groups[1] != '|':
109
+ dbs.append({'schema': groups[1], 'name': groups[2]})
110
+
111
+ if default_schema:
112
+ dbs = [db for db in dbs if db["schema"] == PostgresDatabases.default_schema()]
113
+
114
+ return dbs
115
+
116
+ def run_sql(state: ReplState, sql: str, database: str = None, show_out = True, backgrounded = False):
117
+ if not database:
118
+ database = PostgresDatabases.database(state)
119
+ if not database:
120
+ database = PostgresDatabases.default_db()
121
+
122
+ username = PostgresDatabases.username(state)
123
+ password = PostgresDatabases.password(state)
124
+ endpoint = PostgresDatabases.endpoint(state)
125
+
126
+ if KubeContext.in_cluster():
127
+ cmd1 = f'env PGPASSWORD={password} psql -h {endpoint} -p {PostgresDatabases.port()} -U {username} {database} --pset pager=off -c'
128
+ log2(f'{cmd1} "{sql}"')
129
+ # remove double quotes from the sql argument
130
+ cmd = cmd1.split(' ') + [sql]
131
+
132
+ r = subprocess.run(cmd, capture_output=not backgrounded, text=True)
133
+ if show_out:
134
+ log2(r.stdout)
135
+ log2(r.stderr)
136
+
137
+ return r
138
+ else:
139
+ pod_name, container_name = PostgresDatabases.pod_and_container(state.namespace)
140
+ if not pod_name:
141
+ return
142
+
143
+ cmd = f'psql -h {endpoint} -p {PostgresDatabases.port(state)} -U {username} {database} --pset pager=off -c "{sql}"'
144
+ env_prefix = f'PGPASSWORD="{password}"'
145
+
146
+ r = Pods.exec(pod_name, container_name, state.namespace, cmd, show_out=show_out, backgrounded=backgrounded, env_prefix=env_prefix)
147
+ if r and r.log_file:
148
+ ReplSession().append_history(f':cat {r.log_file}')
149
+
150
+ return r
151
+
152
+ @functools.lru_cache()
153
+ def pod_and_container(namespace: str):
154
+ container_name = Config().get('pg.agent.name', 'ops-pg-agent')
155
+
156
+ if Config().get('pg.agent.just-in-time', False):
157
+ if not PostgresDatabases.deploy_pg_agent(container_name, namespace):
158
+ return None
159
+
160
+ pod_name = container_name
161
+ try:
162
+ # try with dedicated pg agent pod name configured
163
+ Pods.get(namespace, container_name)
164
+ except:
165
+ try:
166
+ # try with the ops pod
167
+ container_name = Config().get('pod.name', 'ops')
168
+ pod_name = Pods.get_with_selector(namespace, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
169
+ except:
170
+ log2(f"Could not locate {container_name} pod.")
171
+ return None
172
+
173
+ return pod_name, container_name
174
+
175
+ def deploy_pg_agent(pod_name: str, namespace: str) -> str:
176
+ image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
177
+ timeout = Config().get('pg.agent.timeout', 3600)
178
+ try:
179
+ Pods.create(namespace, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': namespace}, sa_name='c3')
180
+ except Exception as e:
181
+ if e.status == 409:
182
+ if Pods.completed(namespace, pod_name):
183
+ with log_exc(lambda e2: "Exception when calling BatchV1Api->create_pod: %s\n" % e2):
184
+ Pods.delete(pod_name, namespace)
185
+ Pods.create(namespace, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': namespace}, sa_name='c3')
186
+
187
+ return
188
+ else:
189
+ log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
190
+
191
+ return
192
+
193
+ Pods.wait_for_running(namespace, pod_name)
194
+
195
+ return pod_name
196
+
197
+ def undeploy_pg_agent(pod_name: str, namespace: str):
198
+ Pods.delete(pod_name, namespace, grace_period_seconds=0)
199
+
200
+ def endpoint(state: ReplState):
201
+ return PostgresDatabases._connection_property(state, 'pg.secret.endpoint-key', 'postgres-db-endpoint')
202
+
203
+ def port(state: ReplState):
204
+ return PostgresDatabases._connection_property(state, 'pg.secret.port-key', 'postgres-db-port')
205
+
206
+ def username(state: ReplState):
207
+ return PostgresDatabases._connection_property(state, 'pg.secret.username-key', 'postgres-admin-username')
208
+
209
+ def password(state: ReplState):
210
+ return PostgresDatabases._connection_property(state, 'pg.secret.password-key', 'postgres-admin-password')
211
+
212
+ def _connection_property(state: ReplState, config_key: str, default: str, host: str = None, database: str = None):
213
+ with pg_path(state, host=host, database=database) as (host, _):
214
+ if not (conn := PostgresDatabases.conn_details(state.namespace, host)):
215
+ return ''
216
+
217
+ key = Config().get(config_key, default)
218
+ return conn[key] if key in conn else ''
219
+
220
+ def default_db():
221
+ return Config().get('pg.default-db', 'postgres')
222
+
223
+ def default_owner():
224
+ return Config().get('pg.default-owner', 'postgres')
225
+
226
+ def default_schema():
227
+ return Config().get('pg.default-schema', 'postgres')
228
+
229
+ def host(state: ReplState):
230
+ if not state.pg_path:
231
+ return None
232
+
233
+ return state.pg_path.split('/')[0]
234
+
235
+ def database(state: ReplState):
236
+ if not state.pg_path:
237
+ return None
238
+
239
+ tokens = state.pg_path.split('/')
240
+ if len(tokens) > 1:
241
+ return tokens[1]
242
+
243
+ return None
244
+
245
+ @functools.lru_cache()
246
+ def conn_details(namespace: str, host: str):
247
+ return Secrets.get_data(namespace, host)
248
+
249
+ class PostgresPathHandler:
250
+ def __init__(self, state: ReplState, host: str = None, database: str = None):
251
+ self.state = state
252
+ self.host = host
253
+ self.database = database
254
+
255
+ def __enter__(self) -> tuple[str, str]:
256
+ if self.state and self.state.pg_path:
257
+ host_n_db = self.state.pg_path.split('/')
258
+ if not self.host:
259
+ self.host = host_n_db[0]
260
+ if not self.database and len(host_n_db) > 1:
261
+ self.database = host_n_db[1]
262
+
263
+ return self.host, self.database
264
+
265
+ def __exit__(self, exc_type, exc_val, exc_tb):
266
+ return False
267
+
268
+ def pg_path(state: ReplState, host: str = None, database: str = None):
269
+ return PostgresPathHandler(state, host=host, database=database)
@@ -1,10 +1,9 @@
1
1
  from adam.commands.command import Command
2
- from adam.commands.ls import Ls
2
+ from adam.commands.fs.ls import Ls
3
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:
@@ -4,7 +4,6 @@ from adam.repl_state import ReplState, RequiredState
4
4
 
5
5
  class PostgresPreview(Command):
6
6
  COMMAND = 'pg preview'
7
- reaper_login = None
8
7
 
9
8
  # the singleton pattern
10
9
  def __new__(cls, *args, **kwargs):
@@ -19,21 +18,18 @@ class PostgresPreview(Command):
19
18
  return PostgresPreview.COMMAND
20
19
 
21
20
  def required(self):
22
- return RequiredState.NAMESPACE
21
+ return RequiredState.PG_DATABASE
23
22
 
24
23
  def run(self, cmd: str, state: ReplState):
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, RequiredState.PG_DATABASE):
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
- PreviewTable().run(f'preview {" ".join(args)}', state)
30
+ PreviewTable().run(f'preview {" ".join(args)}', state)
35
31
 
36
- return state
32
+ return state
37
33
 
38
34
  def completion(self, state: ReplState):
39
35
  if state.sts:
@@ -0,0 +1,80 @@
1
+ import functools
2
+
3
+ from adam.commands.postgres.postgres_databases import PostgresDatabases, pg_path
4
+ from adam.config import Config
5
+ from adam.repl_state import ReplState
6
+ from adam.utils import log2, wait_log
7
+ from adam.utils_k8s.pods import Pods
8
+
9
+ TestPG = [False]
10
+
11
+ def direct_dirs(state: ReplState) -> list[str]:
12
+ with pg_path(state) as (host, database):
13
+ if database:
14
+ return ['..']
15
+ elif host:
16
+ return ['..'] + pg_database_names(state)
17
+ else:
18
+ return PostgresDatabases.host_names(state.namespace)
19
+
20
+ def pg_database_names(state: ReplState):
21
+ # cache on pg_path
22
+ return _pg_database_names(state, state.pg_path)
23
+
24
+ @functools.lru_cache()
25
+ def _pg_database_names(state: ReplState, pg_path: str):
26
+ if TestPG[0]:
27
+ return ['azops88_c3ai_c3']
28
+
29
+ wait_log('Inspecting Postgres Databases...')
30
+
31
+ return [db['name'] for db in PostgresDatabases.databases(state, default_owner=True)]
32
+
33
+ def pg_table_names(state: ReplState):
34
+ # cache on pg_path
35
+ return _pg_table_names(state, state.pg_path)
36
+
37
+ @functools.lru_cache()
38
+ def _pg_table_names(state: ReplState, pg_path: str):
39
+ if TestPG[0]:
40
+ return ['C3_2_XYZ1']
41
+
42
+ wait_log('Inspecting Postgres Database...')
43
+ return [table['name'] for table in PostgresDatabases.tables(state, default_schema=True)]
44
+
45
+ class PostgresPodService:
46
+ def __init__(self, handler: 'PostgresExecHandler'):
47
+ self.handler = handler
48
+
49
+ def exec(self, command: str, show_out=True):
50
+ state = self.handler.state
51
+
52
+ pod, container = PostgresDatabases.pod_and_container(state.namespace)
53
+ if not pod:
54
+ log2('Cannot locate postgres agent or ops pod.')
55
+ return state
56
+
57
+ return Pods.exec(pod, container, state.namespace, command, show_out=show_out)
58
+
59
+ def sql(self, args: list[str], backgrounded=False):
60
+ state = self.handler.state
61
+
62
+ query = args
63
+ if isinstance(args, list):
64
+ query = ' '.join(args)
65
+
66
+ PostgresDatabases.run_sql(state, query, show_out=Config().is_debug(), backgrounded=backgrounded)
67
+
68
+ class PostgresExecHandler:
69
+ def __init__(self, state: ReplState, backgrounded=False):
70
+ self.state = state
71
+ self.backgrounded = backgrounded
72
+
73
+ def __enter__(self):
74
+ return PostgresPodService(self)
75
+
76
+ def __exit__(self, exc_type, exc_val, exc_tb):
77
+ return False
78
+
79
+ def postgres(state: ReplState, backgrounded=False):
80
+ return PostgresExecHandler(state, backgrounded=backgrounded)
@@ -1,10 +1,7 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
- from adam.commands.cql.cql_utils import cassandra_table_names, run_cql
3
- from adam.commands.postgres.postgres_context import PostgresContext
4
- from adam.config import Config
3
+ from adam.commands.devices.devices import Devices
5
4
  from adam.repl_state import ReplState, RequiredState
6
- from adam.utils import lines_to_tabular, log, log2
7
- from adam.utils_audits import Audits
8
5
 
9
6
  class PreviewTable(Command):
10
7
  COMMAND = 'preview'
@@ -22,53 +19,20 @@ class PreviewTable(Command):
22
19
  return PreviewTable.COMMAND
23
20
 
24
21
  def required(self):
25
- return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L]
22
+ return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L, RequiredState.EXPORT_DB]
26
23
 
27
24
  def run(self, cmd: str, state: ReplState):
28
25
  if not(args := self.args(cmd)):
29
26
  return super().run(cmd, state)
30
27
 
31
- state, args = self.apply_state(args, state)
32
- if not self.validate_state(state):
33
- return state
34
-
35
- if not args:
36
- def show_tables():
37
- if state.device == ReplState.P:
38
- pg = PostgresContext.apply(state.namespace, state.pg_path)
39
- lines = [db["name"] for db in pg.tables() if db["schema"] == PostgresContext.default_schema()]
40
- log(lines_to_tabular(lines, separator=','))
41
- elif state.device == ReplState.L:
42
- log(lines_to_tabular(Audits.audit_table_names(), separator=','))
43
- else:
44
- log(lines_to_tabular(cassandra_table_names(state), separator=','))
45
-
46
- if state.in_repl:
47
- log2('Table is required.')
48
- log2()
49
- log2('Tables:')
50
- show_tables()
51
- else:
52
- log2('* Table is missing.')
53
- show_tables()
54
-
55
- Command.display_help()
28
+ with self.validate(args, state) as (args, state):
29
+ with validate_args(args, state, at_least=1) as table:
30
+ Devices.of(state).preview(table, state)
56
31
 
57
- return 'command-missing'
58
-
59
- table = args[0]
60
-
61
- rows = Config().get('preview.rows', 10)
62
- if state.device == ReplState.P:
63
- PostgresContext.apply(state.namespace, state.pg_path).run_sql(f'select * from {table} limit {rows}')
64
- elif state.device == ReplState.L:
65
- Audits.run_audit_query(f'select * from {table} limit {rows}')
66
- else:
67
- run_cql(state, f'select * from {table} limit {rows}', show_out=True, use_single_quotes=True, on_any=True)
68
-
69
- return state
32
+ return state
70
33
 
71
34
  def completion(self, _: ReplState):
35
+ # taken care of by the sql completer
72
36
  return {}
73
37
 
74
38
  def help(self, _: ReplState):
@@ -1,6 +1,6 @@
1
1
  import click
2
2
 
3
- from adam.commands.command import Command
3
+ from adam.commands.intermediate_command import IntermediateCommand
4
4
  from .reaper_forward import ReaperForward
5
5
  from .reaper_forward_stop import ReaperForwardStop
6
6
  from .reaper_restart import ReaperRestart
@@ -12,11 +12,9 @@ from .reaper_schedule_start import ReaperScheduleStart
12
12
  from .reaper_schedule_stop import ReaperScheduleStop
13
13
  from .reaper_schedules import ReaperSchedules
14
14
  from .reaper_status import ReaperStatus
15
- from adam.repl_state import ReplState, RequiredState
16
15
 
17
- class Reaper(Command):
16
+ class Reaper(IntermediateCommand):
18
17
  COMMAND = 'reaper'
19
- reaper_login = None
20
18
 
21
19
  # the singleton pattern
22
20
  def __new__(cls, *args, **kwargs):
@@ -24,35 +22,14 @@ class Reaper(Command):
24
22
 
25
23
  return cls.instance
26
24
 
27
- def __init__(self, successor: Command=None):
28
- super().__init__(successor)
29
-
30
25
  def command(self):
31
26
  return Reaper.COMMAND
32
27
 
33
- def required(self):
34
- return RequiredState.CLUSTER
35
-
36
- def run(self, cmd: str, state: ReplState):
37
- if not(args := self.args(cmd)):
38
- return super().run(cmd, state)
39
-
40
- if not self.validate_state(state):
41
- return state
42
-
43
- return super().intermediate_run(cmd, state, args, Reaper.cmd_list())
44
-
45
- def cmd_list():
28
+ def cmd_list(self):
46
29
  return [ReaperSchedules(), ReaperScheduleStop(), ReaperScheduleActivate(), ReaperScheduleStart(),
47
30
  ReaperForwardStop(), ReaperForward(), ReaperRunAbort(), ReaperRunsAbort(), ReaperRestart(),
48
31
  ReaperRuns(), ReaperStatus()]
49
32
 
50
- def completion(self, state: ReplState):
51
- if state.sts:
52
- return super().completion(state)
53
-
54
- return {}
55
-
56
33
  class ReaperCommandHelper(click.Command):
57
34
  def get_help(self, ctx: click.Context):
58
- Command.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper.cmd_list(), show_cluster_help=True)
35
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper().cmd_list(), show_cluster_help=True)