kaqing 1.77.0__py3-none-any.whl → 2.0.171__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. adam/__init__.py +1 -0
  2. adam/app_session.py +182 -0
  3. {walker → adam}/apps.py +8 -24
  4. {walker → adam}/batch.py +54 -97
  5. {walker → adam}/checks/check.py +3 -3
  6. {walker → adam}/checks/check_result.py +1 -1
  7. adam/checks/check_utils.py +65 -0
  8. {walker → adam}/checks/compactionstats.py +6 -6
  9. {walker → adam}/checks/cpu.py +14 -8
  10. adam/checks/cpu_metrics.py +52 -0
  11. {walker → adam}/checks/disk.py +6 -6
  12. {walker → adam}/checks/gossip.py +5 -5
  13. {walker → adam}/checks/memory.py +7 -7
  14. {walker → adam}/checks/status.py +5 -5
  15. {walker → adam}/cli.py +3 -3
  16. {walker → adam}/columns/column.py +1 -1
  17. adam/columns/columns.py +45 -0
  18. {walker → adam}/columns/compactions.py +5 -5
  19. {walker → adam}/columns/cpu.py +6 -4
  20. adam/columns/cpu_metrics.py +22 -0
  21. {walker → adam}/columns/dir_data.py +3 -3
  22. {walker → adam}/columns/dir_snapshots.py +3 -3
  23. {walker → adam}/columns/gossip.py +5 -5
  24. {walker → adam}/columns/host_id.py +3 -3
  25. {walker → adam}/columns/memory.py +3 -3
  26. {walker → adam}/columns/node_address.py +3 -3
  27. {walker → adam}/columns/node_load.py +3 -3
  28. {walker → adam}/columns/node_owns.py +3 -3
  29. {walker → adam}/columns/node_status.py +3 -3
  30. {walker → adam}/columns/node_tokens.py +3 -3
  31. {walker → adam}/columns/node_utils.py +2 -2
  32. {walker → adam}/columns/pod_name.py +2 -2
  33. {walker → adam}/columns/volume_cassandra.py +4 -4
  34. {walker → adam}/columns/volume_root.py +3 -3
  35. adam/commands/__init__.py +15 -0
  36. adam/commands/alter_tables.py +81 -0
  37. adam/commands/app_cmd.py +38 -0
  38. {walker → adam}/commands/app_ping.py +10 -16
  39. adam/commands/audit/audit.py +84 -0
  40. adam/commands/audit/audit_repair_tables.py +74 -0
  41. adam/commands/audit/audit_run.py +50 -0
  42. adam/commands/audit/show_last10.py +48 -0
  43. adam/commands/audit/show_slow10.py +47 -0
  44. adam/commands/audit/show_top10.py +45 -0
  45. adam/commands/audit/utils_show_top10.py +59 -0
  46. adam/commands/bash/__init__.py +5 -0
  47. adam/commands/bash/bash.py +36 -0
  48. adam/commands/bash/bash_completer.py +93 -0
  49. adam/commands/bash/utils_bash.py +16 -0
  50. adam/commands/cat.py +50 -0
  51. adam/commands/cd.py +43 -0
  52. adam/commands/check.py +73 -0
  53. {walker → adam}/commands/cli_commands.py +7 -8
  54. adam/commands/code.py +57 -0
  55. adam/commands/command.py +190 -0
  56. {walker → adam}/commands/command_helpers.py +1 -1
  57. {walker → adam}/commands/commands_utils.py +15 -25
  58. adam/commands/cp.py +89 -0
  59. adam/commands/cql/cql_completions.py +33 -0
  60. {walker/commands → adam/commands/cql}/cqlsh.py +20 -35
  61. adam/commands/cql/utils_cql.py +343 -0
  62. {walker/commands/frontend → adam/commands/deploy}/code_start.py +11 -14
  63. adam/commands/deploy/code_stop.py +40 -0
  64. {walker/commands/frontend → adam/commands/deploy}/code_utils.py +7 -9
  65. adam/commands/deploy/deploy.py +25 -0
  66. adam/commands/deploy/deploy_frontend.py +49 -0
  67. adam/commands/deploy/deploy_pg_agent.py +35 -0
  68. adam/commands/deploy/deploy_pod.py +108 -0
  69. adam/commands/deploy/deploy_utils.py +29 -0
  70. adam/commands/deploy/undeploy.py +25 -0
  71. adam/commands/deploy/undeploy_frontend.py +38 -0
  72. adam/commands/deploy/undeploy_pg_agent.py +39 -0
  73. adam/commands/deploy/undeploy_pod.py +48 -0
  74. adam/commands/devices/device.py +118 -0
  75. adam/commands/devices/device_app.py +173 -0
  76. adam/commands/devices/device_auit_log.py +49 -0
  77. adam/commands/devices/device_cass.py +185 -0
  78. adam/commands/devices/device_export.py +86 -0
  79. adam/commands/devices/device_postgres.py +144 -0
  80. adam/commands/devices/devices.py +25 -0
  81. {walker → adam}/commands/exit.py +3 -6
  82. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  83. adam/commands/export/clean_up_export_sessions.py +51 -0
  84. adam/commands/export/drop_export_database.py +55 -0
  85. adam/commands/export/drop_export_databases.py +43 -0
  86. adam/commands/export/export.py +53 -0
  87. adam/commands/export/export_databases.py +170 -0
  88. adam/commands/export/export_handlers.py +71 -0
  89. adam/commands/export/export_select.py +81 -0
  90. adam/commands/export/export_select_x.py +54 -0
  91. adam/commands/export/export_use.py +52 -0
  92. adam/commands/export/exporter.py +352 -0
  93. adam/commands/export/import_session.py +40 -0
  94. adam/commands/export/importer.py +67 -0
  95. adam/commands/export/importer_athena.py +80 -0
  96. adam/commands/export/importer_sqlite.py +47 -0
  97. adam/commands/export/show_column_counts.py +54 -0
  98. adam/commands/export/show_export_databases.py +36 -0
  99. adam/commands/export/show_export_session.py +48 -0
  100. adam/commands/export/show_export_sessions.py +44 -0
  101. adam/commands/export/utils_export.py +314 -0
  102. {walker → adam}/commands/help.py +17 -12
  103. adam/commands/intermediate_command.py +49 -0
  104. adam/commands/issues.py +43 -0
  105. adam/commands/kubectl.py +38 -0
  106. adam/commands/login.py +70 -0
  107. {walker → adam}/commands/logs.py +8 -10
  108. adam/commands/ls.py +41 -0
  109. adam/commands/medusa/medusa.py +27 -0
  110. adam/commands/medusa/medusa_backup.py +57 -0
  111. adam/commands/medusa/medusa_restore.py +83 -0
  112. adam/commands/medusa/medusa_show_backupjobs.py +51 -0
  113. adam/commands/medusa/medusa_show_restorejobs.py +47 -0
  114. {walker → adam}/commands/nodetool.py +17 -21
  115. {walker → adam}/commands/param_get.py +15 -16
  116. adam/commands/param_set.py +43 -0
  117. adam/commands/postgres/postgres.py +104 -0
  118. adam/commands/postgres/postgres_context.py +274 -0
  119. {walker → adam}/commands/postgres/postgres_ls.py +7 -11
  120. {walker → adam}/commands/postgres/postgres_preview.py +8 -13
  121. adam/commands/postgres/psql_completions.py +10 -0
  122. adam/commands/postgres/utils_postgres.py +66 -0
  123. adam/commands/preview_table.py +37 -0
  124. adam/commands/pwd.py +47 -0
  125. adam/commands/reaper/reaper.py +35 -0
  126. adam/commands/reaper/reaper_forward.py +93 -0
  127. adam/commands/reaper/reaper_forward_session.py +6 -0
  128. {walker → adam}/commands/reaper/reaper_forward_stop.py +13 -19
  129. {walker → adam}/commands/reaper/reaper_restart.py +10 -17
  130. adam/commands/reaper/reaper_run_abort.py +46 -0
  131. adam/commands/reaper/reaper_runs.py +82 -0
  132. adam/commands/reaper/reaper_runs_abort.py +63 -0
  133. adam/commands/reaper/reaper_schedule_activate.py +45 -0
  134. adam/commands/reaper/reaper_schedule_start.py +45 -0
  135. adam/commands/reaper/reaper_schedule_stop.py +45 -0
  136. {walker → adam}/commands/reaper/reaper_schedules.py +6 -16
  137. {walker → adam}/commands/reaper/reaper_status.py +11 -19
  138. adam/commands/reaper/utils_reaper.py +196 -0
  139. adam/commands/repair/repair.py +26 -0
  140. {walker → adam}/commands/repair/repair_log.py +7 -10
  141. adam/commands/repair/repair_run.py +70 -0
  142. adam/commands/repair/repair_scan.py +71 -0
  143. {walker → adam}/commands/repair/repair_stop.py +8 -11
  144. adam/commands/report.py +61 -0
  145. adam/commands/restart.py +60 -0
  146. {walker → adam}/commands/rollout.py +25 -30
  147. adam/commands/shell.py +34 -0
  148. adam/commands/show/show.py +39 -0
  149. walker/commands/show/show_version.py → adam/commands/show/show_adam.py +14 -10
  150. adam/commands/show/show_app_actions.py +57 -0
  151. {walker → adam}/commands/show/show_app_id.py +12 -15
  152. {walker → adam}/commands/show/show_app_queues.py +9 -12
  153. adam/commands/show/show_cassandra_repairs.py +38 -0
  154. adam/commands/show/show_cassandra_status.py +124 -0
  155. {walker → adam}/commands/show/show_cassandra_version.py +6 -16
  156. adam/commands/show/show_commands.py +59 -0
  157. walker/commands/show/show_storage.py → adam/commands/show/show_host.py +11 -13
  158. adam/commands/show/show_login.py +62 -0
  159. {walker → adam}/commands/show/show_params.py +4 -4
  160. adam/commands/show/show_processes.py +51 -0
  161. adam/commands/show/show_storage.py +42 -0
  162. adam/commands/watch.py +82 -0
  163. {walker → adam}/config.py +10 -22
  164. {walker → adam}/embedded_apps.py +1 -1
  165. adam/embedded_params.py +2 -0
  166. adam/log.py +47 -0
  167. {walker → adam}/pod_exec_result.py +10 -2
  168. adam/repl.py +182 -0
  169. adam/repl_commands.py +124 -0
  170. adam/repl_state.py +458 -0
  171. adam/sql/__init__.py +0 -0
  172. adam/sql/sql_completer.py +120 -0
  173. adam/sql/sql_state_machine.py +618 -0
  174. adam/sql/term_completer.py +76 -0
  175. adam/sso/__init__.py +0 -0
  176. {walker → adam}/sso/authenticator.py +5 -1
  177. adam/sso/authn_ad.py +170 -0
  178. {walker → adam}/sso/authn_okta.py +39 -22
  179. adam/sso/cred_cache.py +60 -0
  180. adam/sso/id_token.py +23 -0
  181. adam/sso/idp.py +143 -0
  182. adam/sso/idp_login.py +50 -0
  183. adam/sso/idp_session.py +55 -0
  184. adam/sso/sso_config.py +63 -0
  185. adam/utils.py +679 -0
  186. adam/utils_app.py +98 -0
  187. adam/utils_athena.py +145 -0
  188. adam/utils_audits.py +106 -0
  189. adam/utils_issues.py +32 -0
  190. adam/utils_k8s/__init__.py +0 -0
  191. adam/utils_k8s/app_clusters.py +28 -0
  192. adam/utils_k8s/app_pods.py +33 -0
  193. adam/utils_k8s/cassandra_clusters.py +36 -0
  194. adam/utils_k8s/cassandra_nodes.py +33 -0
  195. adam/utils_k8s/config_maps.py +34 -0
  196. {walker/k8s_utils → adam/utils_k8s}/custom_resources.py +7 -2
  197. adam/utils_k8s/deployment.py +56 -0
  198. {walker/k8s_utils → adam/utils_k8s}/ingresses.py +3 -4
  199. {walker/k8s_utils → adam/utils_k8s}/jobs.py +3 -3
  200. adam/utils_k8s/k8s.py +87 -0
  201. {walker/k8s_utils → adam/utils_k8s}/kube_context.py +4 -4
  202. adam/utils_k8s/pods.py +290 -0
  203. {walker/k8s_utils → adam/utils_k8s}/secrets.py +8 -4
  204. adam/utils_k8s/service_accounts.py +170 -0
  205. {walker/k8s_utils → adam/utils_k8s}/services.py +3 -4
  206. {walker/k8s_utils → adam/utils_k8s}/statefulsets.py +6 -16
  207. {walker/k8s_utils → adam/utils_k8s}/volumes.py +10 -1
  208. adam/utils_net.py +24 -0
  209. adam/utils_repl/__init__.py +0 -0
  210. adam/utils_repl/automata_completer.py +48 -0
  211. adam/utils_repl/repl_completer.py +46 -0
  212. adam/utils_repl/state_machine.py +173 -0
  213. adam/utils_sqlite.py +109 -0
  214. adam/version.py +5 -0
  215. {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/METADATA +1 -1
  216. kaqing-2.0.171.dist-info/RECORD +236 -0
  217. kaqing-2.0.171.dist-info/entry_points.txt +3 -0
  218. kaqing-2.0.171.dist-info/top_level.txt +1 -0
  219. kaqing-1.77.0.dist-info/RECORD +0 -159
  220. kaqing-1.77.0.dist-info/entry_points.txt +0 -3
  221. kaqing-1.77.0.dist-info/top_level.txt +0 -1
  222. walker/__init__.py +0 -3
  223. walker/app_session.py +0 -168
  224. walker/checks/check_utils.py +0 -97
  225. walker/columns/columns.py +0 -43
  226. walker/commands/add_user.py +0 -68
  227. walker/commands/app.py +0 -67
  228. walker/commands/bash.py +0 -87
  229. walker/commands/cd.py +0 -115
  230. walker/commands/check.py +0 -68
  231. walker/commands/command.py +0 -104
  232. walker/commands/cp.py +0 -95
  233. walker/commands/cql_utils.py +0 -53
  234. walker/commands/devices.py +0 -89
  235. walker/commands/frontend/code_stop.py +0 -57
  236. walker/commands/frontend/setup.py +0 -60
  237. walker/commands/frontend/setup_frontend.py +0 -58
  238. walker/commands/frontend/teardown.py +0 -61
  239. walker/commands/frontend/teardown_frontend.py +0 -42
  240. walker/commands/issues.py +0 -69
  241. walker/commands/login.py +0 -72
  242. walker/commands/ls.py +0 -145
  243. walker/commands/medusa/medusa.py +0 -69
  244. walker/commands/medusa/medusa_backup.py +0 -61
  245. walker/commands/medusa/medusa_restore.py +0 -86
  246. walker/commands/medusa/medusa_show_backupjobs.py +0 -52
  247. walker/commands/medusa/medusa_show_restorejobs.py +0 -52
  248. walker/commands/param_set.py +0 -44
  249. walker/commands/postgres/postgres.py +0 -113
  250. walker/commands/postgres/postgres_session.py +0 -225
  251. walker/commands/preview_table.py +0 -98
  252. walker/commands/processes.py +0 -53
  253. walker/commands/pwd.py +0 -64
  254. walker/commands/reaper/reaper.py +0 -78
  255. walker/commands/reaper/reaper_forward.py +0 -100
  256. walker/commands/reaper/reaper_run_abort.py +0 -65
  257. walker/commands/reaper/reaper_runs.py +0 -97
  258. walker/commands/reaper/reaper_runs_abort.py +0 -83
  259. walker/commands/reaper/reaper_schedule_activate.py +0 -64
  260. walker/commands/reaper/reaper_schedule_start.py +0 -64
  261. walker/commands/reaper/reaper_schedule_stop.py +0 -64
  262. walker/commands/reaper/reaper_session.py +0 -159
  263. walker/commands/repair/repair.py +0 -68
  264. walker/commands/repair/repair_run.py +0 -72
  265. walker/commands/repair/repair_scan.py +0 -79
  266. walker/commands/report.py +0 -57
  267. walker/commands/restart.py +0 -61
  268. walker/commands/show/show.py +0 -72
  269. walker/commands/show/show_app_actions.py +0 -53
  270. walker/commands/show/show_cassandra_status.py +0 -35
  271. walker/commands/show/show_commands.py +0 -58
  272. walker/commands/show/show_processes.py +0 -35
  273. walker/commands/show/show_repairs.py +0 -47
  274. walker/commands/status.py +0 -128
  275. walker/commands/storage.py +0 -52
  276. walker/commands/user_entry.py +0 -69
  277. walker/commands/watch.py +0 -85
  278. walker/embedded_params.py +0 -2
  279. walker/k8s_utils/cassandra_clusters.py +0 -48
  280. walker/k8s_utils/cassandra_nodes.py +0 -26
  281. walker/k8s_utils/pods.py +0 -211
  282. walker/repl.py +0 -165
  283. walker/repl_commands.py +0 -58
  284. walker/repl_state.py +0 -211
  285. walker/sso/authn_ad.py +0 -94
  286. walker/sso/idp.py +0 -150
  287. walker/sso/idp_login.py +0 -29
  288. walker/sso/sso_config.py +0 -45
  289. walker/utils.py +0 -194
  290. walker/version.py +0 -5
  291. {walker → adam}/checks/__init__.py +0 -0
  292. {walker → adam}/checks/check_context.py +0 -0
  293. {walker → adam}/checks/issue.py +0 -0
  294. {walker → adam}/cli_group.py +0 -0
  295. {walker → adam}/columns/__init__.py +0 -0
  296. {walker/commands → adam/commands/audit}/__init__.py +0 -0
  297. {walker/commands/frontend → adam/commands/cql}/__init__.py +0 -0
  298. {walker/commands/medusa → adam/commands/deploy}/__init__.py +0 -0
  299. {walker/commands/postgres → adam/commands/devices}/__init__.py +0 -0
  300. {walker/commands/reaper → adam/commands/export}/__init__.py +0 -0
  301. {walker/commands/repair → adam/commands/medusa}/__init__.py +0 -0
  302. {walker → adam}/commands/nodetool_commands.py +0 -0
  303. {walker/commands/show → adam/commands/postgres}/__init__.py +0 -0
  304. {walker/k8s_utils → adam/commands/reaper}/__init__.py +0 -0
  305. {walker/sso → adam/commands/repair}/__init__.py +0 -0
  306. /walker/medusa_show_restorejobs.py → /adam/commands/show/__init__.py +0 -0
  307. {walker → adam}/repl_session.py +0 -0
  308. {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/WHEEL +0 -0
@@ -0,0 +1,196 @@
1
+ from collections.abc import Callable
2
+ from functools import partial
3
+ from typing import List, cast
4
+ from kubernetes import client
5
+ import re
6
+
7
+ import requests
8
+ from adam.config import Config
9
+ from adam.repl_state import ReplState
10
+ from adam.utils import lines_to_tabular, log2, wait_log
11
+ from adam.utils_k8s.k8s import port_forwarding
12
+
13
+ class ReaperService:
14
+ def __init__(self, state: ReplState, local_addr: str, remote_addr: str, show_out = True):
15
+ self.state = state
16
+ self.local_addr = local_addr
17
+ self.remote_addr = remote_addr
18
+ self.show_out = show_out
19
+ self.headers = None
20
+
21
+ def get(self, path: str, params: dict[str, any] = {}):
22
+ with logging(self, 'GET', path) as (url, headers):
23
+ return requests.get(url, headers=headers, params=params)
24
+
25
+ def put(self, path: str, params: dict[str, any] = {}):
26
+ with logging(self, 'PUT', path) as (url, headers):
27
+ return requests.put(url, headers=headers, params=params)
28
+
29
+ def post(self, path: str, params: dict[str, any] = {}):
30
+ with logging(self, 'POST', path) as (url, headers):
31
+ return requests.post(url, headers=headers, params=params)
32
+
33
+ class ReaperLogginHandler:
34
+ def __init__(self, svc: ReaperService, method: str, path: str):
35
+ self.svc = svc
36
+ self.method = method
37
+ self.path = path
38
+
39
+ def __enter__(self) -> tuple[str, dict[str, any]]:
40
+ if not self.svc.headers:
41
+ self.svc.headers = Reapers.cookie_header(self.svc.state, self.svc.local_addr, self.svc.remote_addr, show_output=self.svc.show_out)
42
+
43
+ if self.svc.show_out and self.method:
44
+ log2(f'{self.method} {self.svc.remote_addr}/{self.path}')
45
+
46
+ return (f'http://{self.svc.local_addr}/{self.path}', self.svc.headers)
47
+
48
+ def __exit__(self, exc_type, exc_val, exc_tb):
49
+ if exc_val and isinstance(exc_val, requests.Response):
50
+ if int(exc_val.status_code / 100) != 2:
51
+ if self.svc.show_out:
52
+ log2(exc_val.status_code)
53
+
54
+ return False
55
+
56
+ def logging(svc: ReaperService, method: str, path: str):
57
+ return ReaperLogginHandler(svc, method, path)
58
+
59
+ class ReaperHandler:
60
+ def __init__(self, state: ReplState, show_out = True):
61
+ self.state = state
62
+ self.show_out = show_out
63
+ self.headers = None
64
+ self.forwarding = None
65
+
66
+ def __enter__(self):
67
+ self.forwarding = port_forwarding(self.state, Reapers.local_port(), partial(Reapers.svc_or_pod, self.state), Reapers.target_port())
68
+ (local_addr, remote_addr) = self.forwarding.__enter__()
69
+
70
+ return ReaperService(self.state, local_addr, remote_addr, show_out=self.show_out)
71
+
72
+ def __exit__(self, exc_type, exc_val, exc_tb):
73
+ if self.forwarding:
74
+ return self.forwarding.__exit__(exc_type, exc_val, exc_tb)
75
+
76
+ return False
77
+
78
+ def reaper(state: ReplState, show_out = True):
79
+ return ReaperHandler(state, show_out=show_out)
80
+
81
+ class Reapers:
82
+ schedules_ids_by_cluster: dict[str, list[str]] = {}
83
+
84
+ def pod_name(state: ReplState):
85
+ pods = Reapers.list_reaper_pods(state.sts if state.sts else state.pod, state.namespace)
86
+ if pods:
87
+ return pods[0].metadata.name
88
+
89
+ return None
90
+
91
+ def show_schedule(state: ReplState, schedule_id: str):
92
+ def filter(schedules: list[dict]):
93
+ return [schedule for schedule in schedules if schedule['id'] == schedule_id]
94
+
95
+ Reapers.show_schedules(state, filter)
96
+
97
+ def show_schedules(state: ReplState, filter: Callable[[list[dict]], dict] = None):
98
+ schedules = Reapers.list_schedules(state, filter=filter)
99
+
100
+ # forced refresh of schedule list
101
+ if not filter:
102
+ Reapers.schedules_ids_by_cluster[state.sts] = [schedule['id'] for schedule in schedules]
103
+ Reapers.show_schedules_tabular(schedules)
104
+
105
+ def schedule_ids(state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None):
106
+ schedules = Reapers.list_schedules(state, show_output=show_output, filter=filter)
107
+ return [schedule['id'] for schedule in schedules]
108
+
109
+ def list_schedules(state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None) -> list[dict]:
110
+ with reaper(state, show_out=show_output) as requests:
111
+ if not (response := requests.get('repair_schedule')):
112
+ return
113
+
114
+ res = response.json()
115
+ if filter:
116
+ res = filter(res)
117
+
118
+ return res
119
+
120
+ def show_schedules_tabular(schedules: list[dict]):
121
+ log2(lines_to_tabular([f"{schedule['id']} {schedule['state']} {schedule['cluster_name']} {schedule['keyspace_name']}" for schedule in schedules], 'ID STATE CLUSTER KEYSPACE'))
122
+
123
+ def list_reaper_pods(sts_name: str, namespace: str) -> List[client.V1Pod]:
124
+ v1 = client.CoreV1Api()
125
+
126
+ # k8ssandra.io/reaper: cs-d0767a536f-cs-d0767a536f-reaper
127
+ groups = re.match(Config().get('reaper.pod.cluster-regex', r'(.*?-.*?-.*?-.*?)-.*'), sts_name)
128
+ label_selector = Config().get('reaper.pod.label-selector', 'k8ssandra.io/reaper={cluster}-reaper').replace('{cluster}', groups[1])
129
+
130
+ return cast(List[client.V1Pod], v1.list_namespaced_pod(namespace, label_selector=label_selector).items)
131
+
132
+ def cookie_header(state: ReplState, local_addr, remote_addr, show_output = True):
133
+ return {'Cookie': Reapers.login(state, local_addr, remote_addr, show_output=show_output)}
134
+
135
+ def login(state: ReplState, local_addr: str, remote_addr: str, show_output = True) -> str :
136
+ user, pw = state.user_pass(secret_path='reaper.secret')
137
+
138
+ response = requests.post(f'http://{local_addr}/login', headers={
139
+ 'Accept': '*'
140
+ },data={
141
+ 'username':user,
142
+ 'password':pw})
143
+ if show_output:
144
+ log2(f'POST {remote_addr}/login')
145
+ log2(f' username={user}&password={pw}')
146
+
147
+ if int(response.status_code / 100) != 2:
148
+ if show_output:
149
+ log2("login failed")
150
+ return None
151
+
152
+ return response.headers['Set-Cookie']
153
+
154
+ def reaper_spec(state: ReplState) -> dict[str, any]:
155
+ if not (pod := Reapers.pod_name(state)):
156
+ return {}
157
+
158
+ user, pw = state.user_pass(secret_path='reaper.secret')
159
+
160
+ return {
161
+ 'pod': pod,
162
+ 'exec': f'kubectl exec -it {pod} -n {state.namespace} -- bash',
163
+ 'forward': f'kubectl port-forward pods/{pod} -n {state.namespace} {Reapers.local_port()}:{Reapers.target_port()}',
164
+ 'web-uri': f'http://localhost:{Reapers.local_port()}/webui',
165
+ 'username': user,
166
+ 'password': pw
167
+ }
168
+
169
+ def cached_schedule_ids(state: ReplState) -> list[str]:
170
+ if state.sts in Reapers.schedules_ids_by_cluster:
171
+ return Reapers.schedules_ids_by_cluster[state.sts]
172
+
173
+ if pod := Reapers.pod_name(state):
174
+ wait_log('Inspecting Cassandra Reaper...')
175
+
176
+ schedules = Reapers.schedule_ids(state, show_output = False)
177
+ Reapers.schedules_ids_by_cluster[state.sts] = schedules
178
+
179
+ return schedules
180
+
181
+ return []
182
+
183
+ def svc_name():
184
+ return Config().get('reaper.service-name', 'reaper-service')
185
+
186
+ def local_port():
187
+ return Config().get('reaper.port-forward.local-port', 9001)
188
+
189
+ def target_port():
190
+ return 8080
191
+
192
+ def svc_or_pod(state: ReplState, is_service: bool):
193
+ if is_service:
194
+ return Reapers.svc_name()
195
+ else:
196
+ return Reapers.pod_name(state)
@@ -0,0 +1,26 @@
1
+ import click
2
+
3
+ from adam.commands.intermediate_command import IntermediateCommand
4
+ from .repair_run import RepairRun
5
+ from .repair_scan import RepairScan
6
+ from .repair_stop import RepairStop
7
+ from .repair_log import RepairLog
8
+
9
+ class Repair(IntermediateCommand):
10
+ COMMAND = 'repair'
11
+
12
+ # the singleton pattern
13
+ def __new__(cls, *args, **kwargs):
14
+ if not hasattr(cls, 'instance'): cls.instance = super(Repair, cls).__new__(cls)
15
+
16
+ return cls.instance
17
+
18
+ def command(self):
19
+ return Repair.COMMAND
20
+
21
+ def cmd_list(self):
22
+ return [RepairRun(), RepairScan(), RepairStop(), RepairLog()]
23
+
24
+ class RepairCommandHelper(click.Command):
25
+ def get_help(self, ctx: click.Context):
26
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Repair.COMMAND, Repair().cmd_list(), show_cluster_help=True)
@@ -1,6 +1,6 @@
1
- from walker.commands.command import Command
2
- from walker.k8s_utils.jobs import Jobs
3
- from walker.repl_state import ReplState, RequiredState
1
+ from adam.commands.command import Command
2
+ from adam.utils_k8s.jobs import Jobs
3
+ from adam.repl_state import ReplState, RequiredState
4
4
 
5
5
  class RepairLog(Command):
6
6
  COMMAND = 'repair log'
@@ -24,14 +24,11 @@ class RepairLog(Command):
24
24
  if not(args := self.args(cmd)):
25
25
  return super().run(cmd, state)
26
26
 
27
- state, args = self.apply_state(args, state)
28
- if not self.validate_state(state):
29
- return state
30
-
31
- ns = state.namespace
32
- Jobs.get_logs('cassrepair-'+state.sts, ns)
27
+ with self.validate(args, state) as (args, state):
28
+ ns = state.namespace
29
+ Jobs.get_logs('cassrepair-'+state.sts, ns)
33
30
 
34
- return state
31
+ return state
35
32
 
36
33
  def completion(self, state: ReplState):
37
34
  if state.sts:
@@ -0,0 +1,70 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.reaper.utils_reaper import Reapers
3
+ from adam.utils_k8s.jobs import Jobs
4
+ from adam.utils_k8s.volumes import Volumes
5
+ from adam.repl_state import ReplState, RequiredState
6
+ from adam.config import Config
7
+ from adam.commands.reaper.reaper_runs_abort import ReaperRunsAbort
8
+ from adam.commands.reaper.reaper_schedule_stop import ReaperScheduleStop
9
+ from adam.utils import log2
10
+
11
+ class RepairRun(Command):
12
+ COMMAND = 'repair run'
13
+
14
+ # the singleton pattern
15
+ def __new__(cls, *args, **kwargs):
16
+ if not hasattr(cls, 'instance'): cls.instance = super(RepairRun, cls).__new__(cls)
17
+
18
+ return cls.instance
19
+
20
+ def __init__(self, successor: Command=None):
21
+ super().__init__(successor)
22
+
23
+ def command(self):
24
+ return RepairRun.COMMAND
25
+
26
+ def required(self):
27
+ return RequiredState.CLUSTER
28
+
29
+ def run(self, cmd: str, state: ReplState):
30
+ if not(args := self.args(cmd)):
31
+ return super().run(cmd, state)
32
+
33
+ with self.validate(args, state) as (args, state):
34
+ replace = False
35
+ if len(args) == 1:
36
+ replace = args[0] == 'replace'
37
+
38
+ try:
39
+ log2("Stopping all reaper schedules...")
40
+ for schedule_id in Reapers.cached_schedule_ids(state):
41
+ ReaperScheduleStop().run(f'reaper stop schedule {schedule_id}', state)
42
+ log2("Aborting all reaper runs...")
43
+ state = ReaperRunsAbort().run('reaper abort runs', state)
44
+ except:
45
+ pass
46
+
47
+ image = Config().get('repair.image', 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.11')
48
+ secret = Config().get('repair.secret', 'ciregistryc3iotio')
49
+ log_path = Config().get('repair.log-path', '/home/cassrepair/logs/')
50
+ user, _ = state.user_pass()
51
+ ns = state.namespace
52
+ env = Config().get('repair.env', {})
53
+ env["cluster"] = ns
54
+ env_from = {"username": user, "password": user}
55
+ pvc_name ='cassrepair-log-' + state.sts
56
+ Volumes.create_pvc(pvc_name, 30, ns)
57
+ if replace:
58
+ Jobs.delete('cassrepair-'+state.sts, ns)
59
+ Jobs.create('cassrepair-'+state.sts, ns, image, secret, env, env_from, 'cassrepair', pvc_name, log_path)
60
+
61
+ return state
62
+
63
+ def completion(self, state: ReplState):
64
+ if state.sts:
65
+ return super().completion(state)
66
+
67
+ return {}
68
+
69
+ def help(self, _: ReplState):
70
+ return f'{RepairRun.COMMAND} [replace]\t start a repair job, default not replacing'
@@ -0,0 +1,71 @@
1
+ import time
2
+
3
+ from adam.commands.command import Command
4
+ from adam.utils_k8s.pods import Pods
5
+ from adam.repl_state import ReplState, RequiredState
6
+ from adam.utils import log2
7
+ from adam.config import Config
8
+
9
+ class RepairScan(Command):
10
+ COMMAND = 'repair scan'
11
+
12
+ # the singleton pattern
13
+ def __new__(cls, *args, **kwargs):
14
+ if not hasattr(cls, 'instance'): cls.instance = super(RepairScan, cls).__new__(cls)
15
+
16
+ return cls.instance
17
+
18
+ def __init__(self, successor: Command=None):
19
+ super().__init__(successor)
20
+
21
+ def command(self):
22
+ return RepairScan.COMMAND
23
+
24
+ def required(self):
25
+ return RequiredState.CLUSTER
26
+
27
+ def run(self, cmd: str, state: ReplState):
28
+ if not(args := self.args(cmd)):
29
+ return super().run(cmd, state)
30
+
31
+ with self.validate(args, state) as (args, state):
32
+ n = "7"
33
+ if len(args) == 1:
34
+ n = str(args[0])
35
+ image = Config().get('repair.image', 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.11')
36
+ secret = Config().get('repair.secret', 'ciregistryc3iotio')
37
+ log_path = secret = Config().get('repair.log-path', '/home/cassrepair/logs/')
38
+ ns = state.namespace
39
+ pvc_name ='cassrepair-log-' + state.sts
40
+ pod_name = 'repair-scan'
41
+
42
+ try:
43
+ Pods.create(ns, pod_name, image, ["sh", "-c", "tail -f /dev/null"],
44
+ secret=secret,
45
+ env={},
46
+ volume_name='cassrepair-log',
47
+ pvc_name=pvc_name,
48
+ mount_path='/home/cassrepair/logs/')
49
+ except Exception as e:
50
+ if e.status == 409:
51
+ log2(f"Pod {pod_name} already exists")
52
+ else:
53
+ log2("Exception when calling BatchV1Apii->create_namespaced_job: %s\n" % e)
54
+
55
+ Pods.wait_for_running(ns, pod_name, 'Waiting for the scanner pod to start up...')
56
+
57
+ try:
58
+ Pods.exec(pod_name, pod_name, ns, f"find {log_path} -type f -mtime -{n} -print0 | xargs -0 grep failed")
59
+ finally:
60
+ Pods.delete(pod_name, ns)
61
+
62
+ return state
63
+
64
+ def completion(self, state: ReplState):
65
+ if state.sts:
66
+ return super().completion(state)
67
+
68
+ return {}
69
+
70
+ def help(self, _: ReplState):
71
+ return f'{RepairScan.COMMAND} [n]\t scan last n days repair log, default 7 days'
@@ -1,7 +1,7 @@
1
- from walker.commands.command import Command
2
- from walker.k8s_utils.jobs import Jobs
3
- from walker.repl_state import ReplState, RequiredState
4
- from walker.config import Config
1
+ from adam.commands.command import Command
2
+ from adam.utils_k8s.jobs import Jobs
3
+ from adam.repl_state import ReplState, RequiredState
4
+ from adam.config import Config
5
5
 
6
6
  class RepairStop(Command):
7
7
  COMMAND = 'repair stop'
@@ -25,14 +25,11 @@ class RepairStop(Command):
25
25
  if not(args := self.args(cmd)):
26
26
  return super().run(cmd, state)
27
27
 
28
- state, args = self.apply_state(args, state)
29
- if not self.validate_state(state):
30
- return state
31
-
32
- ns = state.namespace
33
- Jobs.delete('cassrepair-'+state.sts, ns)
28
+ with self.validate(args, state) as (args, state):
29
+ ns = state.namespace
30
+ Jobs.delete('cassrepair-'+state.sts, ns)
34
31
 
35
- return state
32
+ return state
36
33
 
37
34
  def completion(self, state: ReplState):
38
35
  if state.sts:
@@ -0,0 +1,61 @@
1
+ import click
2
+ import json
3
+
4
+ from adam.checks.check_result import CheckResult
5
+ from adam.checks.check_utils import run_checks
6
+ from adam.commands.command import Command
7
+ from adam.repl_state import ReplState
8
+ from adam.utils import log2
9
+
10
+ class Report(Command):
11
+ COMMAND = 'report'
12
+
13
+ # the singleton pattern
14
+ def __new__(cls, *args, **kwargs):
15
+ if not hasattr(cls, 'instance'): cls.instance = super(Report, 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 Report.COMMAND
24
+
25
+ def required(self):
26
+ return ReplState.NON_L
27
+
28
+ def run(self, cmd: str, state: ReplState):
29
+ if not(args := self.args(cmd)):
30
+ return super().run(cmd, state)
31
+
32
+ with self.validate(args, state) as (args, state):
33
+ output: dict[str, any] = {}
34
+
35
+ if state.in_repl:
36
+ args, show = Command.extract_options(args, ['-s', '--show'])
37
+
38
+ args, redirect = Command.extract_options(args, ['>'])
39
+ if not redirect or not args:
40
+ log2('Please specify file name: e.g. report > /tmp/report.log')
41
+ return 'no-report-destination'
42
+
43
+ results = run_checks(state.sts, state.namespace, state.pod, show_out=show)
44
+ output = CheckResult.report(results)
45
+ with open(args[0], "w") as json_file:
46
+ json.dump(output, json_file, indent=2)
47
+ log2(f'Report stored in {args[0]}.')
48
+ else:
49
+ args, show = Command.extract_options(args, ['-s', '--show'])
50
+
51
+ results = run_checks(state.sts, state.namespace, state.pod, show_out=show)
52
+ output = CheckResult.report(results)
53
+ click.echo(json.dumps(output, indent=2))
54
+
55
+ return output
56
+
57
+ def completion(self, state: ReplState):
58
+ return super().completion(state, {">": None})
59
+
60
+ def help(self, _: ReplState):
61
+ return f"{Report.COMMAND} > <file-name>\t generate report"
@@ -0,0 +1,60 @@
1
+ from adam.commands import extract_options
2
+ from adam.commands.command import Command
3
+ from adam.utils_k8s.pods import Pods
4
+ from adam.utils_k8s.statefulsets import StatefulSets
5
+ from adam.repl_state import ReplState, RequiredState
6
+ from adam.utils import log2
7
+
8
+ class Restart(Command):
9
+ COMMAND = 'restart'
10
+
11
+ # the singleton pattern
12
+ def __new__(cls, *args, **kwargs):
13
+ if not hasattr(cls, 'instance'): cls.instance = super(Restart, cls).__new__(cls)
14
+
15
+ return cls.instance
16
+
17
+ def __init__(self, successor: Command=None):
18
+ super().__init__(successor)
19
+
20
+ def command(self):
21
+ return Restart.COMMAND
22
+
23
+ def required(self):
24
+ return RequiredState.CLUSTER_OR_POD
25
+
26
+ def run(self, cmd: str, state: ReplState):
27
+ if not(args := self.args(cmd)):
28
+ return super().run(cmd, state)
29
+
30
+ with self.validate(args, state) as (args, state):
31
+ with extract_options(args, '--force') as (args, forced):
32
+ if not args:
33
+ if state.pod:
34
+ log2(f'Restarting {state.pod}...')
35
+ Pods.delete(state.pod, state.namespace)
36
+ else:
37
+ if not forced:
38
+ log2('Please add --force for restarting all nodes in a cluster.')
39
+ return 'force-needed'
40
+
41
+ log2(f'Restarting all pods from {state.sts}...')
42
+ for pod_name in StatefulSets.pod_names(state.sts, state.namespace):
43
+ Pods.delete(pod_name, state.namespace)
44
+ else:
45
+ for arg in args:
46
+ Pods.delete(arg, state.namespace)
47
+
48
+ return state
49
+
50
+ def completion(self, state: ReplState):
51
+ if super().completion(state):
52
+ if state.pod:
53
+ return {Restart.COMMAND: None}
54
+ elif state.sts:
55
+ return {Restart.COMMAND: {p: None for p in StatefulSets.pod_names(state.sts, state.namespace)}}
56
+
57
+ return {}
58
+
59
+ def help(self, _: ReplState):
60
+ return f"{Restart.COMMAND} [<host-id>] [--force]\t restart the node you're in or all the nodes in the cluster"
@@ -2,12 +2,13 @@ import datetime
2
2
  from kubernetes import client
3
3
  from kubernetes.client.rest import ApiException
4
4
 
5
- from walker.commands.command import Command
6
- from walker.commands.watch import Watch
7
- from walker.k8s_utils.statefulsets import StatefulSets
8
- from walker.config import Config
9
- from walker.repl_state import ReplState, RequiredState
10
- from walker.utils import duration, log2
5
+ from adam.commands import extract_options
6
+ from adam.commands.command import Command
7
+ from adam.commands.watch import Watch
8
+ from adam.utils_k8s.statefulsets import StatefulSets
9
+ from adam.config import Config
10
+ from adam.repl_state import ReplState, RequiredState
11
+ from adam.utils import duration, log2
11
12
 
12
13
  class RollOut(Command):
13
14
  COMMAND = 'rollout'
@@ -31,32 +32,28 @@ class RollOut(Command):
31
32
  if not(args := self.args(cmd)):
32
33
  return super().run(cmd, state)
33
34
 
34
- state, args = self.apply_state(args, state)
35
- if not self.validate_state(state):
36
- return state
35
+ with self.validate(args, state) as (args, state):
36
+ with extract_options(args, '--force') as (args, forced):
37
+ restarted, rollingout = StatefulSets.restarted_at(state.sts, state.namespace)
38
+ if rollingout and not forced:
39
+ log2(f"* Cluster is being rolled out for {duration(restarted)}. Please wait until it's done or use --force.")
37
40
 
38
- args, forced = Command.extract_options(args, '--force')
41
+ return state
39
42
 
40
- restarted, rollingout = StatefulSets.restarted_at(state.sts, state.namespace)
41
- if rollingout and not forced:
42
- log2(f"* Cluster is being rolled out for {duration(restarted)}. Please wait until it's done or use --force.")
43
+ self.rolling_restart(state.sts, state.namespace)
43
44
 
44
- return state
45
+ auto_watch = False
46
+ if (auto_watch_cmds := Config().get('watch.auto', 'rollout')):
47
+ cmds = [c.strip(' ') for c in auto_watch_cmds.split(',')]
48
+ if self.command() in cmds:
49
+ auto_watch = True
50
+ log2('Rolling out cluster with auto watch...')
51
+ Watch().run('watch', state)
45
52
 
46
- self.rolling_restart(state.sts, state.namespace)
53
+ if not auto_watch:
54
+ log2('Rolling out cluster...')
47
55
 
48
- auto_watch = False
49
- if (auto_watch_cmds := Config().get('watch.auto', 'rollout')):
50
- cmds = [c.strip(' ') for c in auto_watch_cmds.split(',')]
51
- if self.command() in cmds:
52
- auto_watch = True
53
- log2('Rolling out cluster with auto watch...')
54
- Watch().run('watch', state)
55
-
56
- if not auto_watch:
57
- log2('Rolling out cluster...')
58
-
59
- return state
56
+ return state
60
57
 
61
58
  def rolling_restart(self, statefulset, namespace):
62
59
  # kubectl rollout restart statefulset <statefulset-name>
@@ -82,9 +79,7 @@ class RollOut(Command):
82
79
  log2("Exception when calling AppsV1Api->read_namespaced_statefulset_status: %s\n" % e)
83
80
 
84
81
  def completion(self, state: ReplState):
85
- if state.pod:
86
- return {}
87
- elif state.sts:
82
+ if super().completion(state):
88
83
  return {RollOut.COMMAND: None}
89
84
 
90
85
  return {}
adam/commands/shell.py ADDED
@@ -0,0 +1,34 @@
1
+ import os
2
+
3
+ from adam.commands.command import Command
4
+ from adam.repl_state import ReplState
5
+
6
+ class Shell(Command):
7
+ COMMAND = ':sh'
8
+
9
+ # the singleton pattern
10
+ def __new__(cls, *args, **kwargs):
11
+ if not hasattr(cls, 'instance'): cls.instance = super(Shell, 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 Shell.COMMAND
20
+
21
+ def run(self, cmd: str, state: ReplState):
22
+ if not(args := self.args(cmd)):
23
+ return super().run(cmd, state)
24
+
25
+ with self.validate(args, state):
26
+ os.system('QING_DROPPED=true bash')
27
+
28
+ return state
29
+
30
+ def completion(self, state: ReplState):
31
+ return super().completion(state)
32
+
33
+ def help(self, _: ReplState):
34
+ return f'{Shell.COMMAND}\t drop down to shell'