kaqing 2.0.14__py3-none-any.whl → 2.0.189__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kaqing might be problematic. Click here for more details.

Files changed (228) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -12
  3. adam/apps.py +20 -6
  4. adam/batch.py +16 -6
  5. adam/checks/check_utils.py +19 -49
  6. adam/checks/compactionstats.py +1 -1
  7. adam/checks/cpu.py +9 -3
  8. adam/checks/cpu_metrics.py +52 -0
  9. adam/checks/disk.py +3 -4
  10. adam/checks/gossip.py +1 -1
  11. adam/checks/memory.py +3 -3
  12. adam/checks/status.py +1 -1
  13. adam/columns/columns.py +3 -1
  14. adam/columns/cpu.py +3 -1
  15. adam/columns/cpu_metrics.py +22 -0
  16. adam/columns/memory.py +3 -4
  17. adam/commands/__init__.py +24 -0
  18. adam/commands/alter_tables.py +66 -0
  19. adam/commands/app/app.py +38 -0
  20. adam/commands/{app_ping.py → app/app_ping.py} +8 -14
  21. adam/commands/app/show_app_actions.py +49 -0
  22. adam/commands/{show → app}/show_app_id.py +9 -12
  23. adam/commands/{show → app}/show_app_queues.py +8 -14
  24. adam/commands/app/utils_app.py +106 -0
  25. adam/commands/audit/__init__.py +0 -0
  26. adam/commands/audit/audit.py +67 -0
  27. adam/commands/audit/audit_repair_tables.py +72 -0
  28. adam/commands/audit/audit_run.py +50 -0
  29. adam/commands/audit/completions_l.py +15 -0
  30. adam/commands/audit/show_last10.py +36 -0
  31. adam/commands/audit/show_slow10.py +36 -0
  32. adam/commands/audit/show_top10.py +36 -0
  33. adam/commands/audit/utils_show_top10.py +71 -0
  34. adam/commands/bash/__init__.py +5 -0
  35. adam/commands/bash/bash.py +36 -0
  36. adam/commands/bash/bash_completer.py +93 -0
  37. adam/commands/bash/utils_bash.py +16 -0
  38. adam/commands/cat.py +36 -0
  39. adam/commands/cd.py +14 -88
  40. adam/commands/check.py +18 -21
  41. adam/commands/cli_commands.py +11 -7
  42. adam/commands/clipboard_copy.py +87 -0
  43. adam/commands/code.py +57 -0
  44. adam/commands/command.py +220 -19
  45. adam/commands/commands_utils.py +28 -31
  46. adam/commands/cql/__init__.py +0 -0
  47. adam/commands/cql/completions_c.py +28 -0
  48. adam/commands/{cqlsh.py → cql/cqlsh.py} +13 -32
  49. adam/commands/cql/utils_cql.py +305 -0
  50. adam/commands/deploy/code_start.py +7 -10
  51. adam/commands/deploy/code_stop.py +4 -21
  52. adam/commands/deploy/code_utils.py +5 -5
  53. adam/commands/deploy/deploy.py +4 -40
  54. adam/commands/deploy/deploy_frontend.py +15 -18
  55. adam/commands/deploy/deploy_pg_agent.py +4 -7
  56. adam/commands/deploy/deploy_pod.py +74 -77
  57. adam/commands/deploy/deploy_utils.py +16 -26
  58. adam/commands/deploy/undeploy.py +4 -40
  59. adam/commands/deploy/undeploy_frontend.py +5 -8
  60. adam/commands/deploy/undeploy_pg_agent.py +7 -8
  61. adam/commands/deploy/undeploy_pod.py +16 -17
  62. adam/commands/devices/__init__.py +0 -0
  63. adam/commands/devices/device.py +149 -0
  64. adam/commands/devices/device_app.py +163 -0
  65. adam/commands/devices/device_auit_log.py +49 -0
  66. adam/commands/devices/device_cass.py +179 -0
  67. adam/commands/devices/device_export.py +87 -0
  68. adam/commands/devices/device_postgres.py +160 -0
  69. adam/commands/devices/devices.py +25 -0
  70. adam/commands/download_file.py +47 -0
  71. adam/commands/exit.py +1 -4
  72. adam/commands/export/__init__.py +0 -0
  73. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  74. adam/commands/export/clean_up_export_sessions.py +39 -0
  75. adam/commands/export/completions_x.py +11 -0
  76. adam/commands/export/download_export_session.py +40 -0
  77. adam/commands/export/drop_export_database.py +39 -0
  78. adam/commands/export/drop_export_databases.py +37 -0
  79. adam/commands/export/export.py +37 -0
  80. adam/commands/export/export_databases.py +246 -0
  81. adam/commands/export/export_select.py +34 -0
  82. adam/commands/export/export_sessions.py +209 -0
  83. adam/commands/export/export_use.py +49 -0
  84. adam/commands/export/export_x_select.py +48 -0
  85. adam/commands/export/exporter.py +332 -0
  86. adam/commands/export/import_files.py +44 -0
  87. adam/commands/export/import_session.py +44 -0
  88. adam/commands/export/importer.py +81 -0
  89. adam/commands/export/importer_athena.py +148 -0
  90. adam/commands/export/importer_sqlite.py +67 -0
  91. adam/commands/export/show_column_counts.py +45 -0
  92. adam/commands/export/show_export_databases.py +39 -0
  93. adam/commands/export/show_export_session.py +39 -0
  94. adam/commands/export/show_export_sessions.py +37 -0
  95. adam/commands/export/utils_export.py +344 -0
  96. adam/commands/find_files.py +51 -0
  97. adam/commands/find_processes.py +76 -0
  98. adam/commands/head.py +36 -0
  99. adam/commands/help.py +14 -9
  100. adam/commands/intermediate_command.py +52 -0
  101. adam/commands/issues.py +14 -40
  102. adam/commands/kubectl.py +38 -0
  103. adam/commands/login.py +26 -25
  104. adam/commands/logs.py +5 -7
  105. adam/commands/ls.py +11 -115
  106. adam/commands/medusa/medusa.py +4 -46
  107. adam/commands/medusa/medusa_backup.py +22 -29
  108. adam/commands/medusa/medusa_restore.py +51 -49
  109. adam/commands/medusa/medusa_show_backupjobs.py +20 -21
  110. adam/commands/medusa/medusa_show_restorejobs.py +16 -21
  111. adam/commands/medusa/utils_medusa.py +15 -0
  112. adam/commands/nodetool.py +8 -17
  113. adam/commands/param_get.py +11 -14
  114. adam/commands/param_set.py +9 -13
  115. adam/commands/postgres/completions_p.py +22 -0
  116. adam/commands/postgres/postgres.py +49 -73
  117. adam/commands/postgres/postgres_databases.py +270 -0
  118. adam/commands/postgres/postgres_ls.py +4 -8
  119. adam/commands/postgres/postgres_preview.py +5 -9
  120. adam/commands/postgres/utils_postgres.py +79 -0
  121. adam/commands/preview_table.py +10 -69
  122. adam/commands/pwd.py +14 -43
  123. adam/commands/reaper/reaper.py +6 -49
  124. adam/commands/reaper/reaper_forward.py +49 -56
  125. adam/commands/reaper/reaper_forward_session.py +6 -0
  126. adam/commands/reaper/reaper_forward_stop.py +10 -16
  127. adam/commands/reaper/reaper_restart.py +8 -15
  128. adam/commands/reaper/reaper_run_abort.py +8 -33
  129. adam/commands/reaper/reaper_runs.py +43 -58
  130. adam/commands/reaper/reaper_runs_abort.py +29 -49
  131. adam/commands/reaper/reaper_schedule_activate.py +14 -33
  132. adam/commands/reaper/reaper_schedule_start.py +9 -33
  133. adam/commands/reaper/reaper_schedule_stop.py +9 -33
  134. adam/commands/reaper/reaper_schedules.py +4 -14
  135. adam/commands/reaper/reaper_status.py +8 -16
  136. adam/commands/reaper/utils_reaper.py +203 -0
  137. adam/commands/repair/repair.py +4 -46
  138. adam/commands/repair/repair_log.py +6 -12
  139. adam/commands/repair/repair_run.py +29 -36
  140. adam/commands/repair/repair_scan.py +33 -41
  141. adam/commands/repair/repair_stop.py +6 -13
  142. adam/commands/report.py +25 -21
  143. adam/commands/restart.py +27 -28
  144. adam/commands/rollout.py +20 -25
  145. adam/commands/shell.py +12 -4
  146. adam/commands/show/show.py +15 -46
  147. adam/commands/show/show_adam.py +3 -3
  148. adam/commands/show/show_cassandra_repairs.py +37 -0
  149. adam/commands/show/show_cassandra_status.py +48 -52
  150. adam/commands/show/show_cassandra_version.py +5 -18
  151. adam/commands/show/show_cli_commands.py +56 -0
  152. adam/commands/show/show_host.py +33 -0
  153. adam/commands/show/show_login.py +23 -27
  154. adam/commands/show/show_params.py +2 -5
  155. adam/commands/show/show_processes.py +18 -21
  156. adam/commands/show/show_storage.py +11 -20
  157. adam/commands/watch.py +27 -30
  158. adam/config.py +8 -6
  159. adam/embedded_params.py +1 -1
  160. adam/log.py +4 -4
  161. adam/pod_exec_result.py +13 -5
  162. adam/repl.py +136 -120
  163. adam/repl_commands.py +66 -24
  164. adam/repl_session.py +8 -1
  165. adam/repl_state.py +343 -73
  166. adam/sql/__init__.py +0 -0
  167. adam/sql/lark_completer.py +284 -0
  168. adam/sql/lark_parser.py +604 -0
  169. adam/sql/sql_completer.py +118 -0
  170. adam/sql/sql_state_machine.py +630 -0
  171. adam/sql/term_completer.py +76 -0
  172. adam/sso/authn_ad.py +7 -9
  173. adam/sso/authn_okta.py +4 -6
  174. adam/sso/cred_cache.py +4 -6
  175. adam/sso/idp.py +10 -13
  176. adam/utils.py +539 -11
  177. adam/utils_athena.py +145 -0
  178. adam/utils_audits.py +102 -0
  179. adam/utils_issues.py +32 -0
  180. adam/utils_k8s/__init__.py +0 -0
  181. adam/utils_k8s/app_clusters.py +28 -0
  182. adam/utils_k8s/app_pods.py +36 -0
  183. adam/utils_k8s/cassandra_clusters.py +44 -0
  184. adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
  185. adam/{k8s_utils → utils_k8s}/custom_resources.py +16 -17
  186. adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
  187. adam/{k8s_utils → utils_k8s}/ingresses.py +2 -2
  188. adam/{k8s_utils → utils_k8s}/jobs.py +7 -11
  189. adam/utils_k8s/k8s.py +96 -0
  190. adam/{k8s_utils → utils_k8s}/kube_context.py +3 -3
  191. adam/{k8s_utils → utils_k8s}/pods.py +132 -83
  192. adam/{k8s_utils → utils_k8s}/secrets.py +7 -3
  193. adam/{k8s_utils → utils_k8s}/service_accounts.py +5 -4
  194. adam/{k8s_utils → utils_k8s}/services.py +2 -2
  195. adam/{k8s_utils → utils_k8s}/statefulsets.py +9 -16
  196. adam/utils_local.py +4 -0
  197. adam/utils_net.py +24 -0
  198. adam/utils_repl/__init__.py +0 -0
  199. adam/utils_repl/appendable_completer.py +6 -0
  200. adam/utils_repl/automata_completer.py +48 -0
  201. adam/utils_repl/repl_completer.py +172 -0
  202. adam/utils_repl/state_machine.py +173 -0
  203. adam/utils_sqlite.py +137 -0
  204. adam/version.py +1 -1
  205. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
  206. kaqing-2.0.189.dist-info/RECORD +253 -0
  207. kaqing-2.0.189.dist-info/top_level.txt +2 -0
  208. teddy/__init__.py +0 -0
  209. teddy/lark_parser.py +436 -0
  210. teddy/lark_parser2.py +618 -0
  211. adam/commands/app.py +0 -67
  212. adam/commands/bash.py +0 -87
  213. adam/commands/cp.py +0 -95
  214. adam/commands/cql_utils.py +0 -53
  215. adam/commands/devices.py +0 -89
  216. adam/commands/postgres/postgres_session.py +0 -247
  217. adam/commands/reaper/reaper_session.py +0 -159
  218. adam/commands/show/show_app_actions.py +0 -53
  219. adam/commands/show/show_commands.py +0 -61
  220. adam/commands/show/show_repairs.py +0 -47
  221. adam/k8s_utils/cassandra_clusters.py +0 -48
  222. kaqing-2.0.14.dist-info/RECORD +0 -167
  223. kaqing-2.0.14.dist-info/top_level.txt +0 -1
  224. /adam/{k8s_utils → commands/app}/__init__.py +0 -0
  225. /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
  226. /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
  227. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
  228. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
adam/commands/bash.py DELETED
@@ -1,87 +0,0 @@
1
- from adam.commands.command import Command
2
- from adam.k8s_utils.cassandra_clusters import CassandraClusters
3
- from adam.k8s_utils.cassandra_nodes import CassandraNodes
4
- from adam.repl_state import BashSession, ReplState, RequiredState
5
-
6
- class Bash(Command):
7
- COMMAND = 'bash'
8
-
9
- # the singleton pattern
10
- def __new__(cls, *args, **kwargs):
11
- if not hasattr(cls, 'instance'): cls.instance = super(Bash, cls).__new__(cls)
12
-
13
- return cls.instance
14
-
15
- def __init__(self, successor: Command=None):
16
- super().__init__(successor)
17
-
18
- def command(self):
19
- return Bash.COMMAND
20
-
21
- def required(self):
22
- return RequiredState.CLUSTER_OR_POD
23
-
24
- def run(self, cmd: str, s0: ReplState):
25
- if not(args := self.args(cmd)):
26
- return super().run(cmd, s0)
27
-
28
- state, args = self.apply_state(args, s0)
29
- if not self.validate_state(state):
30
- return state
31
-
32
- if state.in_repl:
33
- r = self.exec_with_dir(s0, args)
34
- if not r:
35
- state.exit_bash()
36
-
37
- return 'inconsistent pwd'
38
-
39
- return r
40
- else:
41
- a = ' '.join(args)
42
- command = f'bash -c "{a}"'
43
-
44
- if state.pod:
45
- CassandraNodes.exec(state.pod, state.namespace, command, show_out=True)
46
- elif state.sts:
47
- CassandraClusters.exec(state.sts, state.namespace, command, action='bash', show_out=True)
48
-
49
- return state
50
-
51
- def exec_with_dir(self, state: ReplState, args: list[str]):
52
- session_just_created = False
53
- if not args:
54
- session_just_created = True
55
- session = BashSession(state.device)
56
- state.enter_bash(session)
57
-
58
- if state.bash_session:
59
- if args != ['pwd']:
60
- if args:
61
- args.append('&&')
62
- args.extend(['pwd', '>', f'/tmp/.qing-{state.bash_session.session_id}'])
63
-
64
- if not session_just_created:
65
- if pwd := state.bash_session.pwd(state):
66
- args = ['cd', pwd, '&&'] + args
67
-
68
- a = ' '.join(args)
69
- command = f'bash -c "{a}"'
70
-
71
- rs = []
72
-
73
- if state.pod:
74
- rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=not session_just_created)]
75
- elif state.sts:
76
- rs = CassandraClusters.exec(state.sts, state.namespace, command, action='bash', show_out=not session_just_created)
77
-
78
- return rs
79
-
80
- def completion(self, state: ReplState):
81
- if state.pod or state.sts:
82
- return {Bash.COMMAND: None}
83
-
84
- return {}
85
-
86
- def help(self, _: ReplState):
87
- return f'{Bash.COMMAND} [bash-commands]\t run bash on the Cassandra nodes'
adam/commands/cp.py DELETED
@@ -1,95 +0,0 @@
1
- import click
2
- import pyperclip
3
-
4
- from adam.commands.command import Command
5
- from adam.commands.command_helpers import ClusterOrPodCommandHelper
6
- from adam.commands.cli_commands import CliCommands
7
- from adam.repl_state import ReplState, RequiredState
8
- from adam.utils import lines_to_tabular, log, log2
9
-
10
- class ClipboardCopy(Command):
11
- COMMAND = 'cp'
12
-
13
- # the singleton pattern
14
- def __new__(cls, *args, **kwargs):
15
- if not hasattr(cls, 'instance'): cls.instance = super(ClipboardCopy, cls).__new__(cls)
16
-
17
- return cls.instance
18
-
19
- def __init__(self, successor: Command=None):
20
- super().__init__(successor)
21
-
22
- def command(self):
23
- return ClipboardCopy.COMMAND
24
-
25
- def required(self):
26
- return RequiredState.CLUSTER_OR_POD
27
-
28
- def run(self, cmd: str, state: ReplState):
29
- if not(args := self.args(cmd)):
30
- return super().run(cmd, state)
31
-
32
- state, args = self.apply_state(args, state)
33
- if not self.validate_state(state):
34
- return state
35
-
36
- if len(args) < 1:
37
- if state.in_repl:
38
- log2('Key is required.')
39
- log2()
40
- log2('Keys:')
41
- log2(lines_to_tabular([f'{k},{v}' for k, v in CliCommands.values(state, collapse=True).items()], separator=','))
42
- else:
43
- log2('* Key is missing.')
44
- Command.display_help()
45
-
46
- return 'command-missing'
47
-
48
- key = args[0]
49
- if not key in CliCommands.values(state):
50
- if state.in_repl:
51
- log2('Key is required.')
52
- log2()
53
- log2('Keys:')
54
- log2(lines_to_tabular([f'{k},{v}' for k, v in CliCommands.values(state, collapse=True).items()], separator=','))
55
- else:
56
- log2('* Invalid key')
57
- Command.display_help()
58
-
59
- return 'command-invalid'
60
-
61
- value = CliCommands.values(state)[key]
62
- pyperclip.copy(value)
63
- log2('The following line has been copied to clipboard. Use <Ctrl-V> to use it.')
64
- log2(f' {value}')
65
-
66
- return 'value-copied'
67
-
68
- def completion(self, state: ReplState):
69
- if state.sts:
70
- return {ClipboardCopy.COMMAND: {key: None for key in CliCommands.values(state).keys()}}
71
-
72
- return {}
73
-
74
- def help(self, _: ReplState):
75
- return f"{ClipboardCopy.COMMAND} <key>\t copy a value to clipboard for conveninence"
76
-
77
- class CopyCommandHelper(click.Command):
78
- def lines(self):
79
- return [
80
- 'node-exec-?: kubectl exec command to the Cassandra pod',
81
- 'reaper-exec: kubectl exec command to the Reaper pod',
82
- 'reaper-forward: kubectl port-forward command to the Reaper pod',
83
- 'reaper-ui: uri to Reaper ui',
84
- 'reaper-username: Reaper user name',
85
- 'reaper-password: Reaper password',
86
- ]
87
-
88
- def get_help(self, ctx: click.Context):
89
- log(super().get_help(ctx))
90
- log()
91
- log('Keys:')
92
-
93
- log(lines_to_tabular(self.lines(), separator=':'))
94
- log()
95
- ClusterOrPodCommandHelper.cluter_or_pod_help()
@@ -1,53 +0,0 @@
1
- import re
2
-
3
- from adam.k8s_utils.cassandra_clusters import CassandraClusters
4
- from adam.k8s_utils.cassandra_nodes import CassandraNodes
5
- from adam.k8s_utils.secrets import Secrets
6
- from adam.repl_state import ReplState
7
-
8
- def run_cql(state: ReplState, cql: str, opts: list = [], show_out = False, use_single_quotes = False):
9
- user, pw = Secrets.get_user_pass(state.sts if state.sts else state.pod, state.namespace, secret_path='cql.secret')
10
- if use_single_quotes:
11
- command = f"cqlsh -u {user} -p {pw} {' '.join(opts)} -e '{cql}'"
12
- else:
13
- command = f'cqlsh -u {user} -p {pw} {" ".join(opts)} -e "{cql}"'
14
-
15
- if state.pod:
16
- return CassandraNodes.exec(state.pod, state.namespace, command, show_out=show_out)
17
- else:
18
- return CassandraClusters.exec(state.sts, state.namespace, command, action='cql')
19
-
20
- def parse_cql_desc_tables(out: str):
21
- # Keyspace data_endpoint_auth
22
- # ---------------------------
23
- # "token"
24
-
25
- # Keyspace reaper_db
26
- # ------------------
27
- # repair_run schema_migration
28
- # repair_run_by_cluster schema_migration_leader
29
-
30
- # Keyspace system
31
- tables_by_keyspace: dict[str, list[str]] = {}
32
- keyspace = None
33
- state = 's0'
34
- for line in out.split('\n'):
35
- if state == 's0':
36
- groups = re.match(r'^Keyspace (.*)$', line)
37
- if groups:
38
- keyspace = groups[1].strip(' \r')
39
- state = 's1'
40
- elif state == 's1':
41
- if line.startswith('---'):
42
- state = 's2'
43
- elif state == 's2':
44
- if not line.strip(' \r'):
45
- state = 's0'
46
- else:
47
- for table in line.split(' '):
48
- if t := table.strip(' \r'):
49
- if not keyspace in tables_by_keyspace:
50
- tables_by_keyspace[keyspace] = []
51
- tables_by_keyspace[keyspace].append(t)
52
-
53
- return tables_by_keyspace
adam/commands/devices.py DELETED
@@ -1,89 +0,0 @@
1
- from adam.commands.command import Command
2
- from adam.repl_state import ReplState
3
-
4
- class DeviceCass(Command):
5
- COMMAND = f'{ReplState.C}:'
6
-
7
- # the singleton pattern
8
- def __new__(cls, *args, **kwargs):
9
- if not hasattr(cls, 'instance'): cls.instance = super(DeviceCass, cls).__new__(cls)
10
-
11
- return cls.instance
12
-
13
- def __init__(self, successor: Command=None):
14
- super().__init__(successor)
15
-
16
- def command(self):
17
- return DeviceCass.COMMAND
18
-
19
- def run(self, cmd: str, state: ReplState):
20
- if not self.args(cmd):
21
- return super().run(cmd, state)
22
-
23
- state.device = ReplState.C
24
-
25
- return state
26
-
27
- def completion(self, state: ReplState):
28
- return super().completion(state)
29
-
30
- def help(self, _: ReplState):
31
- return f'{DeviceCass.COMMAND}\t move to Cassandra Operations device'
32
-
33
- class DevicePostgres(Command):
34
- COMMAND = f'{ReplState.P}:'
35
-
36
- # the singleton pattern
37
- def __new__(cls, *args, **kwargs):
38
- if not hasattr(cls, 'instance'): cls.instance = super(DevicePostgres, cls).__new__(cls)
39
-
40
- return cls.instance
41
-
42
- def __init__(self, successor: Command=None):
43
- super().__init__(successor)
44
-
45
- def command(self):
46
- return DevicePostgres.COMMAND
47
-
48
- def run(self, cmd: str, state: ReplState):
49
- if not self.args(cmd):
50
- return super().run(cmd, state)
51
-
52
- state.device = ReplState.P
53
-
54
- return state
55
-
56
- def completion(self, state: ReplState):
57
- return super().completion(state)
58
-
59
- def help(self, _: ReplState):
60
- return f'{DevicePostgres.COMMAND}\t move to Postgres Operations device'
61
-
62
- class DeviceApp(Command):
63
- COMMAND = f'{ReplState.A}:'
64
-
65
- # the singleton pattern
66
- def __new__(cls, *args, **kwargs):
67
- if not hasattr(cls, 'instance'): cls.instance = super(DeviceApp, cls).__new__(cls)
68
-
69
- return cls.instance
70
-
71
- def __init__(self, successor: Command=None):
72
- super().__init__(successor)
73
-
74
- def command(self):
75
- return DeviceApp.COMMAND
76
-
77
- def run(self, cmd: str, state: ReplState):
78
- if not self.args(cmd):
79
- return super().run(cmd, state)
80
-
81
- state.device = ReplState.A
82
-
83
- return state
84
-
85
- def completion(self, state: ReplState):
86
- return super().completion(state)
87
-
88
- def help(self, _: ReplState):
89
- return f'{DeviceApp.COMMAND}\t move to App Operations device'
@@ -1,247 +0,0 @@
1
- import functools
2
- import re
3
- import subprocess
4
-
5
- from adam.config import Config
6
- from adam.k8s_utils.kube_context import KubeContext
7
- from adam.k8s_utils.pods import Pods
8
- from adam.k8s_utils.secrets import Secrets
9
- from adam.utils import log2
10
-
11
- class PostgresSession:
12
- def __init__(self, ns: str, path: str):
13
- self.namespace = ns
14
- self.conn_details = None
15
- self.host = None
16
- self.db = None
17
-
18
- if path:
19
- tks = path.split('/')
20
- hn = tks[0].split('@')
21
- self.host = hn[0]
22
- if len(hn) > 1 and not ns:
23
- self.namespace = hn[1]
24
-
25
- if len(tks) > 1:
26
- self.db = tks[1]
27
-
28
- # work for databases()
29
- def __eq__(self, other: 'PostgresSession'):
30
- return self.host == other.host
31
-
32
- def __hash__(self):
33
- return hash(self.host)
34
-
35
- def find_namespace(self, arg: str):
36
- if arg:
37
- tks = arg.split('@')
38
- if len(tks) > 1:
39
- return tks[1]
40
-
41
- return None
42
-
43
- def directory(self, arg: str = None):
44
- if arg:
45
- if arg == '..':
46
- if self.db:
47
- self.db = None
48
- else:
49
- self.host = None
50
- else:
51
- tks = arg.split('@')
52
- arg = tks[0]
53
- if not self.host:
54
- self.host = arg
55
- else:
56
- self.db = arg
57
-
58
- if not self.host:
59
- return None
60
-
61
- d = self.host
62
- if not self.db:
63
- return d
64
-
65
- return f'{self.host}/{self.db}'
66
-
67
- def hosts(ns: str):
68
- return PostgresSession.hosts_for_namespace(ns)
69
-
70
- @functools.lru_cache()
71
- def hosts_for_namespace(ns: str):
72
- ss = Secrets.list_secrets(ns, name_pattern=Config().get('pg.name-pattern', '^{namespace}.*k8spg.*'))
73
-
74
- def excludes(name: str):
75
- exs = Config().get('pg.excludes', '.helm., -admin-secret')
76
- if exs:
77
- for ex in exs.split(','):
78
- if ex.strip(' ') in name:
79
- return True
80
-
81
- return False
82
-
83
- return [s for s in ss if not excludes(s)]
84
-
85
- @functools.lru_cache()
86
- def databases(self):
87
- dbs = []
88
- # List of databases
89
- # Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
90
- # ---------------------------------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
91
- # postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
92
- # stgawsscpsr_c3_c3 | postgres | UTF8 | C | C | | libc |
93
- # template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
94
- # | | | | | | | postgres=CTc/postgres
95
- # (48 rows)
96
- if r := self.run_sql('\l', show_out=False):
97
- s = 0
98
- for line in r.stdout.split('\n'):
99
- line: str = line.strip(' \r')
100
- if s == 0:
101
- if 'List of databases' in line:
102
- s = 1
103
- elif s == 1:
104
- if 'Name' in line and 'Owner' in line and 'Encoding' in line:
105
- s = 2
106
- elif s == 2:
107
- if line.startswith('---------'):
108
- s = 3
109
- elif s == 3:
110
- groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
111
- if groups and groups[1] != '|':
112
- dbs.append({'name': groups[1], 'owner': groups[2]})
113
-
114
- return dbs
115
-
116
- def tables(self):
117
- dbs = []
118
- # List of relations
119
- # Schema | Name | Type | Owner
120
- # ----------+------------------------------------------------------------+-------+---------------
121
- # postgres | c3_2_admin_aclpriv | table | postgres
122
- # postgres | c3_2_admin_aclpriv_a | table | postgres
123
- if r := self.run_sql('\dt', show_out=False):
124
- s = 0
125
- for line in r.stdout.split('\n'):
126
- line: str = line.strip(' \r')
127
- if s == 0:
128
- if 'List of relations' in line:
129
- s = 1
130
- elif s == 1:
131
- if 'Schema' in line and 'Name' in line and 'Type' in line:
132
- s = 2
133
- elif s == 2:
134
- if line.startswith('---------'):
135
- s = 3
136
- elif s == 3:
137
- groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
138
- if groups and groups[1] != '|':
139
- dbs.append({'schema': groups[1], 'name': groups[2]})
140
-
141
- return dbs
142
-
143
- def run_sql(self, sql: str, show_out = True):
144
- db = self.db if self.db else PostgresSession.default_db()
145
-
146
- if KubeContext.in_cluster():
147
- cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
148
- log2(f'{cmd1} "{sql}"')
149
- # remove double quotes from the sql argument
150
- cmd = cmd1.split(' ') + [sql]
151
- r = subprocess.run(cmd, capture_output=True, text=True)
152
- if show_out:
153
- log2(r.stdout)
154
- log2(r.stderr)
155
-
156
- return r
157
- else:
158
- ns = self.namespace
159
- pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
160
-
161
- if Config().get('pg.agent.just-in-time', False):
162
- if not PostgresSession.deploy_pg_agent(pod_name, ns):
163
- return
164
-
165
- real_pod_name = pod_name
166
- try:
167
- # try with dedicated pg agent pod name configured
168
- Pods.get(ns, pod_name)
169
- except:
170
- try:
171
- # try with the ops pod
172
- real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
173
- except:
174
- log2(f"Could not locate {pod_name} pod.")
175
- return None
176
-
177
- cmd = f'PGPASSWORD="{self.password()}" psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
178
-
179
- return Pods.exec(real_pod_name, pod_name, ns, cmd, show_out=show_out)
180
-
181
- def deploy_pg_agent(pod_name: str, ns: str) -> str:
182
- image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
183
- timeout = Config().get('pg.agent.timeout', 3600)
184
- try:
185
- Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
186
- except Exception as e:
187
- if e.status == 409:
188
- if Pods.completed(ns, pod_name):
189
- try:
190
- Pods.delete(pod_name, ns)
191
- Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
192
- except Exception as e2:
193
- log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
194
-
195
- return
196
- else:
197
- log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
198
-
199
- return
200
-
201
- Pods.wait_for_running(ns, pod_name)
202
-
203
- return pod_name
204
-
205
- def undeploy_pg_agent(pod_name: str, ns: str):
206
- Pods.delete(pod_name, ns, grace_period_seconds=0)
207
-
208
- def endpoint(self):
209
- if not self.conn_details:
210
- self.conn_details = Secrets.get_data(self.namespace, self.host)
211
-
212
- endpoint_key = Config().get('pg.secret.endpoint-key', 'postgres-db-endpoint')
213
-
214
- return self.conn_details[endpoint_key] if endpoint_key in self.conn_details else ''
215
-
216
- def port(self):
217
- if not self.conn_details:
218
- self.conn_details = Secrets.get_data(self.namespace, self.host)
219
-
220
- port_key = Config().get('pg.secret.port-key', 'postgres-db-port')
221
-
222
- return self.conn_details[port_key] if port_key in self.conn_details else ''
223
-
224
- def username(self):
225
- if not self.conn_details:
226
- self.conn_details = Secrets.get_data(self.namespace, self.host)
227
-
228
- username_key = Config().get('pg.secret.username-key', 'postgres-admin-username')
229
-
230
- return self.conn_details[username_key] if username_key in self.conn_details else ''
231
-
232
- def password(self):
233
- if not self.conn_details:
234
- self.conn_details = Secrets.get_data(self.namespace, self.host)
235
-
236
- password_key = Config().get('pg.secret.password-key', 'postgres-admin-password')
237
-
238
- return self.conn_details[password_key] if password_key in self.conn_details else ''
239
-
240
- def default_db():
241
- return Config().get('pg.default-db', 'postgres')
242
-
243
- def default_owner():
244
- return Config().get('pg.default-owner', 'postgres')
245
-
246
- def default_schema():
247
- return Config().get('pg.default-schema', 'postgres')