kaqing 2.0.98__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 (194) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -7
  3. adam/batch.py +4 -18
  4. adam/checks/check_utils.py +14 -46
  5. adam/checks/cpu.py +7 -1
  6. adam/checks/cpu_metrics.py +52 -0
  7. adam/columns/columns.py +3 -1
  8. adam/columns/cpu.py +3 -1
  9. adam/columns/cpu_metrics.py +22 -0
  10. adam/commands/__init__.py +15 -0
  11. adam/commands/alter_tables.py +50 -61
  12. adam/commands/app_cmd.py +38 -0
  13. adam/commands/app_ping.py +8 -14
  14. adam/commands/audit/audit.py +43 -30
  15. adam/commands/audit/audit_repair_tables.py +26 -46
  16. adam/commands/audit/audit_run.py +50 -0
  17. adam/commands/audit/show_last10.py +48 -0
  18. adam/commands/audit/show_slow10.py +47 -0
  19. adam/commands/audit/show_top10.py +45 -0
  20. adam/commands/audit/utils_show_top10.py +59 -0
  21. adam/commands/bash/__init__.py +5 -0
  22. adam/commands/bash/bash.py +36 -0
  23. adam/commands/bash/bash_completer.py +93 -0
  24. adam/commands/bash/utils_bash.py +16 -0
  25. adam/commands/cat.py +50 -0
  26. adam/commands/cd.py +15 -91
  27. adam/commands/check.py +23 -18
  28. adam/commands/cli_commands.py +2 -3
  29. adam/commands/code.py +57 -0
  30. adam/commands/command.py +96 -40
  31. adam/commands/commands_utils.py +9 -19
  32. adam/commands/cp.py +33 -39
  33. adam/commands/cql/cql_completions.py +30 -8
  34. adam/commands/cql/cqlsh.py +12 -27
  35. adam/commands/cql/utils_cql.py +343 -0
  36. adam/commands/deploy/code_start.py +7 -10
  37. adam/commands/deploy/code_stop.py +4 -21
  38. adam/commands/deploy/code_utils.py +3 -3
  39. adam/commands/deploy/deploy.py +4 -21
  40. adam/commands/deploy/deploy_frontend.py +14 -17
  41. adam/commands/deploy/deploy_pg_agent.py +3 -6
  42. adam/commands/deploy/deploy_pod.py +67 -73
  43. adam/commands/deploy/deploy_utils.py +14 -24
  44. adam/commands/deploy/undeploy.py +4 -21
  45. adam/commands/deploy/undeploy_frontend.py +4 -7
  46. adam/commands/deploy/undeploy_pg_agent.py +6 -8
  47. adam/commands/deploy/undeploy_pod.py +11 -12
  48. adam/commands/devices/device.py +118 -0
  49. adam/commands/devices/device_app.py +173 -0
  50. adam/commands/devices/device_auit_log.py +49 -0
  51. adam/commands/devices/device_cass.py +185 -0
  52. adam/commands/devices/device_export.py +86 -0
  53. adam/commands/devices/device_postgres.py +144 -0
  54. adam/commands/devices/devices.py +25 -0
  55. adam/commands/exit.py +1 -4
  56. adam/commands/export/__init__.py +0 -0
  57. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  58. adam/commands/export/clean_up_export_sessions.py +51 -0
  59. adam/commands/export/drop_export_database.py +55 -0
  60. adam/commands/export/drop_export_databases.py +43 -0
  61. adam/commands/export/export.py +53 -0
  62. adam/commands/export/export_databases.py +170 -0
  63. adam/commands/export/export_handlers.py +71 -0
  64. adam/commands/export/export_select.py +81 -0
  65. adam/commands/export/export_select_x.py +54 -0
  66. adam/commands/export/export_use.py +52 -0
  67. adam/commands/export/exporter.py +352 -0
  68. adam/commands/export/import_session.py +40 -0
  69. adam/commands/export/importer.py +67 -0
  70. adam/commands/export/importer_athena.py +80 -0
  71. adam/commands/export/importer_sqlite.py +47 -0
  72. adam/commands/export/show_column_counts.py +54 -0
  73. adam/commands/export/show_export_databases.py +36 -0
  74. adam/commands/export/show_export_session.py +48 -0
  75. adam/commands/export/show_export_sessions.py +44 -0
  76. adam/commands/export/utils_export.py +314 -0
  77. adam/commands/help.py +10 -6
  78. adam/commands/intermediate_command.py +49 -0
  79. adam/commands/issues.py +14 -40
  80. adam/commands/kubectl.py +38 -0
  81. adam/commands/login.py +28 -24
  82. adam/commands/logs.py +4 -6
  83. adam/commands/ls.py +11 -116
  84. adam/commands/medusa/medusa.py +4 -22
  85. adam/commands/medusa/medusa_backup.py +20 -24
  86. adam/commands/medusa/medusa_restore.py +30 -32
  87. adam/commands/medusa/medusa_show_backupjobs.py +16 -17
  88. adam/commands/medusa/medusa_show_restorejobs.py +12 -17
  89. adam/commands/nodetool.py +11 -17
  90. adam/commands/param_get.py +11 -12
  91. adam/commands/param_set.py +9 -10
  92. adam/commands/postgres/postgres.py +43 -36
  93. adam/commands/postgres/{postgres_session.py → postgres_context.py} +80 -46
  94. adam/commands/postgres/postgres_ls.py +4 -8
  95. adam/commands/postgres/postgres_preview.py +5 -9
  96. adam/commands/postgres/psql_completions.py +2 -2
  97. adam/commands/postgres/utils_postgres.py +66 -0
  98. adam/commands/preview_table.py +8 -61
  99. adam/commands/pwd.py +14 -44
  100. adam/commands/reaper/reaper.py +4 -24
  101. adam/commands/reaper/reaper_forward.py +48 -55
  102. adam/commands/reaper/reaper_forward_session.py +6 -0
  103. adam/commands/reaper/reaper_forward_stop.py +10 -16
  104. adam/commands/reaper/reaper_restart.py +7 -14
  105. adam/commands/reaper/reaper_run_abort.py +11 -30
  106. adam/commands/reaper/reaper_runs.py +42 -57
  107. adam/commands/reaper/reaper_runs_abort.py +29 -49
  108. adam/commands/reaper/reaper_schedule_activate.py +11 -30
  109. adam/commands/reaper/reaper_schedule_start.py +10 -29
  110. adam/commands/reaper/reaper_schedule_stop.py +10 -29
  111. adam/commands/reaper/reaper_schedules.py +4 -14
  112. adam/commands/reaper/reaper_status.py +8 -16
  113. adam/commands/reaper/utils_reaper.py +196 -0
  114. adam/commands/repair/repair.py +4 -22
  115. adam/commands/repair/repair_log.py +4 -7
  116. adam/commands/repair/repair_run.py +27 -29
  117. adam/commands/repair/repair_scan.py +31 -34
  118. adam/commands/repair/repair_stop.py +4 -7
  119. adam/commands/report.py +25 -21
  120. adam/commands/restart.py +25 -26
  121. adam/commands/rollout.py +19 -24
  122. adam/commands/shell.py +5 -4
  123. adam/commands/show/show.py +6 -19
  124. adam/commands/show/show_app_actions.py +26 -22
  125. adam/commands/show/show_app_id.py +8 -11
  126. adam/commands/show/show_app_queues.py +7 -10
  127. adam/commands/show/{show_repairs.py → show_cassandra_repairs.py} +8 -17
  128. adam/commands/show/show_cassandra_status.py +29 -33
  129. adam/commands/show/show_cassandra_version.py +4 -14
  130. adam/commands/show/show_commands.py +19 -21
  131. adam/commands/show/show_host.py +1 -1
  132. adam/commands/show/show_login.py +26 -24
  133. adam/commands/show/show_processes.py +16 -18
  134. adam/commands/show/show_storage.py +10 -20
  135. adam/commands/watch.py +26 -29
  136. adam/config.py +5 -14
  137. adam/embedded_params.py +1 -1
  138. adam/pod_exec_result.py +7 -1
  139. adam/repl.py +95 -131
  140. adam/repl_commands.py +48 -20
  141. adam/repl_state.py +270 -61
  142. adam/sql/sql_completer.py +105 -63
  143. adam/sql/sql_state_machine.py +618 -0
  144. adam/sql/term_completer.py +3 -0
  145. adam/sso/authn_ad.py +6 -5
  146. adam/sso/authn_okta.py +3 -3
  147. adam/sso/cred_cache.py +3 -2
  148. adam/sso/idp.py +3 -3
  149. adam/utils.py +439 -3
  150. adam/utils_app.py +98 -0
  151. adam/utils_athena.py +140 -87
  152. adam/utils_audits.py +106 -0
  153. adam/utils_issues.py +32 -0
  154. adam/utils_k8s/app_clusters.py +28 -0
  155. adam/utils_k8s/app_pods.py +33 -0
  156. adam/utils_k8s/cassandra_clusters.py +22 -20
  157. adam/utils_k8s/cassandra_nodes.py +4 -4
  158. adam/utils_k8s/custom_resources.py +5 -0
  159. adam/utils_k8s/ingresses.py +2 -2
  160. adam/utils_k8s/k8s.py +87 -0
  161. adam/utils_k8s/pods.py +77 -68
  162. adam/utils_k8s/secrets.py +4 -4
  163. adam/utils_k8s/service_accounts.py +5 -4
  164. adam/utils_k8s/services.py +2 -2
  165. adam/utils_k8s/statefulsets.py +1 -12
  166. adam/utils_net.py +4 -4
  167. adam/utils_repl/__init__.py +0 -0
  168. adam/utils_repl/automata_completer.py +48 -0
  169. adam/utils_repl/repl_completer.py +46 -0
  170. adam/utils_repl/state_machine.py +173 -0
  171. adam/utils_sqlite.py +109 -0
  172. adam/version.py +1 -1
  173. {kaqing-2.0.98.dist-info → kaqing-2.0.171.dist-info}/METADATA +1 -1
  174. kaqing-2.0.171.dist-info/RECORD +236 -0
  175. adam/commands/app.py +0 -67
  176. adam/commands/bash.py +0 -92
  177. adam/commands/cql/cql_table_completer.py +0 -8
  178. adam/commands/cql/cql_utils.py +0 -115
  179. adam/commands/describe/describe.py +0 -47
  180. adam/commands/describe/describe_keyspace.py +0 -60
  181. adam/commands/describe/describe_keyspaces.py +0 -49
  182. adam/commands/describe/describe_schema.py +0 -49
  183. adam/commands/describe/describe_table.py +0 -60
  184. adam/commands/describe/describe_tables.py +0 -49
  185. adam/commands/devices.py +0 -118
  186. adam/commands/postgres/postgres_utils.py +0 -31
  187. adam/commands/postgres/psql_table_completer.py +0 -11
  188. adam/commands/reaper/reaper_session.py +0 -159
  189. adam/sql/state_machine.py +0 -460
  190. kaqing-2.0.98.dist-info/RECORD +0 -191
  191. /adam/commands/{describe → devices}/__init__.py +0 -0
  192. {kaqing-2.0.98.dist-info → kaqing-2.0.171.dist-info}/WHEEL +0 -0
  193. {kaqing-2.0.98.dist-info → kaqing-2.0.171.dist-info}/entry_points.txt +0 -0
  194. {kaqing-2.0.98.dist-info → kaqing-2.0.171.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,18 @@
1
1
  import click
2
2
 
3
+ from adam.commands import extract_trailing_options
3
4
  from adam.commands.command import Command
5
+ from adam.commands.intermediate_command import IntermediateCommand
4
6
  from adam.commands.postgres.psql_completions import psql_completions
5
- from adam.commands.postgres.postgres_utils import pg_table_names
7
+ from adam.commands.postgres.utils_postgres import pg_table_names, postgres
6
8
  from .postgres_ls import PostgresLs
7
9
  from .postgres_preview import PostgresPreview
8
- from .postgres_session import PostgresSession
10
+ from .postgres_context import PostgresContext
9
11
  from adam.repl_state import ReplState
10
12
  from adam.utils import log, log2
11
13
 
12
- class Postgres(Command):
14
+ class Postgres(IntermediateCommand):
13
15
  COMMAND = 'pg'
14
- reaper_login = None
15
16
 
16
17
  # the singleton pattern
17
18
  def __new__(cls, *args, **kwargs):
@@ -29,40 +30,36 @@ class Postgres(Command):
29
30
  if not(args := self.args(cmd)):
30
31
  return super().run(cmd, state)
31
32
 
32
- state, args = self.apply_state(args, state)
33
+ with self.validate(args, state) as (args, state):
34
+ with extract_trailing_options(args, '&') as (args, backgrounded):
35
+ if not args:
36
+ if state.in_repl:
37
+ log2('Please use SQL statement. e.g. pg \l')
38
+ else:
39
+ log2('* Command or SQL statements is missing.')
40
+ Command.display_help()
33
41
 
34
- if not args:
35
- if state.in_repl:
36
- log2('Please use SQL statement. e.g. pg \l')
37
- else:
38
- log2('* Command or SQL statements is missing.')
39
- Command.display_help()
42
+ return 'command-missing'
40
43
 
41
- return 'command-missing'
44
+ if not state.pg_path:
45
+ if state.in_repl:
46
+ log2('Enter "use <pg-name>" first.')
47
+ else:
48
+ log2('* pg-name is missing.')
42
49
 
43
- if state.in_repl:
44
- self.run_sql(state, args)
45
- else:
46
- # head with the Chain of Responsibility pattern
47
- cmds = Command.chain(Postgres.cmd_list())
48
- if not cmds.run(cmd, state) :
49
- self.run_sql(state, args)
50
-
51
- return state
52
-
53
- def cmd_list():
54
- return [PostgresLs(), PostgresPreview()]
50
+ return state
55
51
 
56
- def run_sql(self, state: ReplState, args: list[str]):
57
- if not state.pg_path:
58
- if state.in_repl:
59
- log2('Enter "use <pg-name>" first.')
60
- else:
61
- log2('* pg-name is missing.')
52
+ if state.in_repl:
53
+ with postgres(state) as pod:
54
+ pod.sql(args, background=backgrounded)
55
+ elif not self.run_subcommand(cmd, state):
56
+ with postgres(state) as pod:
57
+ pod.sql(args, background=backgrounded)
62
58
 
63
- return state
59
+ return state
64
60
 
65
- PostgresSession(state.namespace, state.pg_path).run_sql(' '.join(args))
61
+ def cmd_list(self):
62
+ return [PostgresLs(), PostgresPreview(), PostgresPg()]
66
63
 
67
64
  def completion(self, state: ReplState):
68
65
  if state.device != state.P:
@@ -70,7 +67,7 @@ class Postgres(Command):
70
67
  return {}
71
68
 
72
69
  leaf = {}
73
- session = PostgresSession(state.namespace, state.pg_path)
70
+ session = PostgresContext.apply(state.namespace, state.pg_path)
74
71
  if session.db:
75
72
  if pg_table_names(state.namespace, state.pg_path):
76
73
  leaf = psql_completions(state.namespace, state.pg_path)
@@ -86,12 +83,22 @@ class Postgres(Command):
86
83
  return {}
87
84
 
88
85
  def help(self, _: ReplState):
89
- return f'[{Postgres.COMMAND}] <sql-statements>\t run psql with queries'
86
+ return f'<sql-statements> [&]\t run queries on Postgres databases'
90
87
 
91
88
  class PostgresCommandHelper(click.Command):
92
89
  def get_help(self, ctx: click.Context):
93
- Command.intermediate_help(super().get_help(ctx), Postgres.COMMAND, Postgres.cmd_list(), show_cluster_help=True)
90
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Postgres.COMMAND, Postgres().cmd_list(), show_cluster_help=True)
94
91
  log('PG-Name: Kubernetes secret for Postgres credentials')
95
92
  log(' e.g. stgawsscpsr-c3-c3-k8spg-cs-001')
96
93
  log('Database: Postgres database name within a host')
97
- log(' e.g. stgawsscpsr_c3_c3')
94
+ log(' e.g. stgawsscpsr_c3_c3')
95
+
96
+ # No action body, only for a help entry and auto-completion
97
+ class PostgresPg(Command):
98
+ COMMAND = 'pg'
99
+
100
+ def command(self):
101
+ return PostgresPg.COMMAND
102
+
103
+ def help(self, _: ReplState):
104
+ return f'pg <sql-statements>\t run queries on Postgres databases'
@@ -1,14 +1,37 @@
1
+ from datetime import datetime
1
2
  import functools
2
3
  import re
3
4
  import subprocess
4
5
 
5
6
  from adam.config import Config
7
+ from adam.repl_session import ReplSession
6
8
  from adam.utils_k8s.kube_context import KubeContext
7
9
  from adam.utils_k8s.pods import Pods
8
10
  from adam.utils_k8s.secrets import Secrets
9
11
  from adam.utils import log2
10
12
 
11
- class PostgresSession:
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
+
12
35
  def __init__(self, ns: str, path: str):
13
36
  self.namespace = ns
14
37
  self.conn_details = None
@@ -25,29 +48,7 @@ class PostgresSession:
25
48
  if len(tks) > 1:
26
49
  self.db = tks[1]
27
50
 
28
- def find_namespace(self, arg: str):
29
- if arg:
30
- tks = arg.split('@')
31
- if len(tks) > 1:
32
- return tks[1]
33
-
34
- return None
35
-
36
- def directory(self, arg: str = None):
37
- if arg:
38
- if arg == '..':
39
- if self.db:
40
- self.db = None
41
- else:
42
- self.host = None
43
- else:
44
- tks = arg.split('@')
45
- arg = tks[0]
46
- if not self.host:
47
- self.host = arg
48
- else:
49
- self.db = arg
50
-
51
+ def path(self):
51
52
  if not self.host:
52
53
  return None
53
54
 
@@ -58,7 +59,7 @@ class PostgresSession:
58
59
  return f'{self.host}/{self.db}'
59
60
 
60
61
  def hosts(ns: str):
61
- return PostgresSession.hosts_for_namespace(ns)
62
+ return PostgresContext.hosts_for_namespace(ns)
62
63
 
63
64
  @functools.lru_cache()
64
65
  def hosts_for_namespace(ns: str):
@@ -132,44 +133,77 @@ class PostgresSession:
132
133
 
133
134
  return dbs
134
135
 
135
- def run_sql(self, sql: str, show_out = True):
136
- db = self.db if self.db else PostgresSession.default_db()
136
+ def run_sql(self, sql: str, show_out = True, background = False):
137
+ db = self.db if self.db else PostgresContext.default_db()
137
138
 
138
139
  if KubeContext.in_cluster():
139
140
  cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
140
141
  log2(f'{cmd1} "{sql}"')
141
142
  # remove double quotes from the sql argument
142
143
  cmd = cmd1.split(' ') + [sql]
143
- r = subprocess.run(cmd, capture_output=True, text=True)
144
+
145
+ r = subprocess.run(cmd, capture_output=not background, text=True)
144
146
  if show_out:
145
147
  log2(r.stdout)
146
148
  log2(r.stderr)
147
149
 
148
150
  return r
149
151
  else:
150
- ns = self.namespace
151
- pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
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
152
185
 
153
- if Config().get('pg.agent.just-in-time', False):
154
- if not PostgresSession.deploy_pg_agent(pod_name, ns):
155
- return
186
+ def pod_and_container(ns: str):
187
+ container_name = Config().get('pg.agent.name', 'ops-pg-agent')
156
188
 
157
- real_pod_name = pod_name
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:
158
198
  try:
159
- # try with dedicated pg agent pod name configured
160
- Pods.get(ns, pod_name)
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
161
202
  except:
162
- try:
163
- # try with the ops pod
164
- pod_name = Config().get('pod.name', 'ops')
165
- real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
166
- except:
167
- log2(f"Could not locate {pod_name} pod.")
168
- return None
169
-
170
- cmd = f'PGPASSWORD="{self.password()}" psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
203
+ log2(f"Could not locate {container_name} pod.")
204
+ return None
171
205
 
172
- return Pods.exec(real_pod_name, pod_name, ns, cmd, show_out=show_out)
206
+ return pod_name, container_name
173
207
 
174
208
  def deploy_pg_agent(pod_name: str, ns: str) -> str:
175
209
  image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
@@ -4,7 +4,6 @@ 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:
@@ -1,4 +1,4 @@
1
- from adam.commands.postgres.postgres_utils import pg_table_names
1
+ from adam.commands.postgres.utils_postgres import pg_table_names
2
2
  from adam.sql.sql_completer import SqlCompleter
3
3
 
4
4
  def psql_completions(ns: str, pg_path: str):
@@ -7,4 +7,4 @@ def psql_completions(ns: str, pg_path: str):
7
7
  '\d': None,
8
8
  '\dt': None,
9
9
  '\du': None
10
- } | SqlCompleter.completions(lambda: pg_table_names(ns, pg_path))
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)
@@ -1,13 +1,6 @@
1
- import functools
2
-
3
1
  from adam.commands.command import Command
4
- from adam.commands.cql.cql_table_completer import CqlTableNameCompleter
5
- from adam.commands.cql.cql_utils import run_cql, table_names, tables
6
- from adam.commands.postgres.postgres_session import PostgresSession
7
- from adam.commands.postgres.psql_table_completer import PsqlTableNameCompleter
8
- from adam.config import Config
2
+ from adam.commands.devices.devices import Devices
9
3
  from adam.repl_state import ReplState, RequiredState
10
- from adam.utils import lines_to_tabular, log, log2
11
4
 
12
5
  class PreviewTable(Command):
13
6
  COMMAND = 'preview'
@@ -25,66 +18,20 @@ class PreviewTable(Command):
25
18
  return PreviewTable.COMMAND
26
19
 
27
20
  def required(self):
28
- return RequiredState.CLUSTER_OR_POD
21
+ return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L]
29
22
 
30
23
  def run(self, cmd: str, state: ReplState):
31
24
  if not(args := self.args(cmd)):
32
25
  return super().run(cmd, state)
33
26
 
34
- state, args = self.apply_state(args, state)
35
- if state.device == ReplState.P:
36
- if not self.validate_state(state, RequiredState.PG_DATABASE):
37
- return state
38
- else:
39
- if not self.validate_state(state):
40
- return state
41
-
42
- if not args:
43
- def show_tables():
44
- if state.device == ReplState.P:
45
- pg = PostgresSession(state.namespace, state.pg_path)
46
- lines = [db["name"] for db in pg.tables() if db["schema"] == PostgresSession.default_schema()]
47
- log(lines_to_tabular(lines, separator=','))
48
- else:
49
- run_cql(state, f'describe tables', show_out=True)
50
-
51
- if state.in_repl:
52
- log2('Table is required.')
53
- log2()
54
- log2('Tables:')
55
- show_tables()
56
- else:
57
- log2('* Table is missing.')
58
- show_tables()
59
-
60
- Command.display_help()
61
-
62
- return 'command-missing'
27
+ with self.validate(args, state) as (args, state):
28
+ Devices.device(state).preview(args[0] if args else None, state)
63
29
 
64
- table = args[0]
65
-
66
- rows = Config().get('preview.rows', 10)
67
- if state.device == ReplState.P:
68
- PostgresSession(state.namespace, state.pg_path).run_sql(f'select * from {table} limit {rows}')
69
- else:
70
- run_cql(state, f'select * from {table} limit {rows}', show_out=True, use_single_quotes=True)
71
-
72
- return state
73
-
74
- def completion(self, state: ReplState):
75
- if state.device == ReplState.P:
76
- return {PreviewTable.COMMAND: PsqlTableNameCompleter(state.namespace, state.pg_path)}
77
- elif state.sts:
78
- return {PreviewTable.COMMAND: CqlTableNameCompleter(table_names(state))}
30
+ return state
79
31
 
32
+ def completion(self, _: ReplState):
33
+ # taken care of by the sql completer
80
34
  return {}
81
35
 
82
36
  def help(self, _: ReplState):
83
- return f'{PreviewTable.COMMAND} TABLE\t preview table'
84
-
85
- @functools.lru_cache()
86
- def cql_tables(state: ReplState):
87
- if state.pod:
88
- return tables(state)
89
-
90
- return tables(state, on_any=True)
37
+ return f'{PreviewTable.COMMAND} TABLE\t preview table'
adam/commands/pwd.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from adam.app_session import AppSession
2
2
  from adam.commands.command import Command
3
- from adam.commands.postgres.postgres_session import PostgresSession
3
+ from adam.commands.devices.devices import Devices
4
4
  from adam.repl_state import ReplState
5
5
  from adam.utils import lines_to_tabular, log
6
6
 
@@ -23,52 +23,22 @@ class Pwd(Command):
23
23
  if not(args := self.args(cmd)):
24
24
  return super().run(cmd, state)
25
25
 
26
- state, _ = self.apply_state(args, state)
27
-
28
- def device_line(state: ReplState, device: str):
29
- words = []
30
-
31
- if device == ReplState.P:
32
- pg = PostgresSession(state.namespace, state.pg_path)
33
-
34
- if pg.host:
35
- words.append(f'host/{pg.host}')
36
- if pg.db:
37
- words.append(f'database/{pg.db}')
38
- elif device == ReplState.A:
39
- if state.app_env:
40
- words.append(f'env/{state.app_env}')
41
- if state.app_app:
42
- words.append(f'app/{state.app_app}')
43
- elif device == ReplState.L:
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:
44
32
  pass
45
- else:
46
- if state.sts:
47
- words.append(f'sts/{state.sts}')
48
- if state.pod:
49
- words.append(f'pod/{state.pod}')
50
-
51
- return '\t'.join([f'{device}:>'] + (words if words else ['/']))
52
-
53
- host = "unknown"
54
- try:
55
- app_session: AppSession = AppSession.create('c3', 'c3')
56
- host = app_session.host
57
- except:
58
- pass
59
33
 
60
- log(lines_to_tabular([
61
- device_line(state, ReplState.A),
62
- device_line(state, ReplState.C),
63
- device_line(state, ReplState.L),
64
- device_line(state, ReplState.P),
65
- f'',
66
- f'HOST\t{host}',
67
- f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
68
- ], 'DEVICE\tLOCATION', separator='\t'))
69
- log()
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()
70
40
 
71
- return state
41
+ return state
72
42
 
73
43
  def completion(self, state: ReplState):
74
44
  return super().completion(state)
@@ -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,32 +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
- return super().intermediate_run(cmd, state, args, Reaper.cmd_list())
41
-
42
- def cmd_list():
28
+ def cmd_list(self):
43
29
  return [ReaperSchedules(), ReaperScheduleStop(), ReaperScheduleActivate(), ReaperScheduleStart(),
44
30
  ReaperForwardStop(), ReaperForward(), ReaperRunAbort(), ReaperRunsAbort(), ReaperRestart(),
45
31
  ReaperRuns(), ReaperStatus()]
46
32
 
47
- def completion(self, state: ReplState):
48
- if state.sts:
49
- return super().completion(state)
50
-
51
- return {}
52
-
53
33
  class ReaperCommandHelper(click.Command):
54
34
  def get_help(self, ctx: click.Context):
55
- 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)