kaqing 2.0.145__py3-none-any.whl → 2.0.172__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 (172) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +8 -11
  3. adam/batch.py +3 -3
  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/checks/disk.py +2 -3
  8. adam/columns/columns.py +3 -1
  9. adam/columns/cpu.py +3 -1
  10. adam/columns/cpu_metrics.py +22 -0
  11. adam/columns/memory.py +3 -4
  12. adam/commands/__init__.py +18 -0
  13. adam/commands/alter_tables.py +43 -47
  14. adam/commands/audit/audit.py +22 -23
  15. adam/commands/audit/audit_repair_tables.py +14 -17
  16. adam/commands/audit/audit_run.py +15 -23
  17. adam/commands/audit/show_last10.py +10 -13
  18. adam/commands/audit/show_slow10.py +10 -13
  19. adam/commands/audit/show_top10.py +10 -13
  20. adam/commands/audit/utils_show_top10.py +2 -3
  21. adam/commands/bash/__init__.py +5 -0
  22. adam/commands/bash/bash.py +7 -104
  23. adam/commands/bash/utils_bash.py +16 -0
  24. adam/commands/cat.py +13 -19
  25. adam/commands/cd.py +8 -10
  26. adam/commands/check.py +20 -21
  27. adam/commands/cli_commands.py +2 -3
  28. adam/commands/code.py +20 -23
  29. adam/commands/command.py +120 -39
  30. adam/commands/commands_utils.py +8 -17
  31. adam/commands/cp.py +33 -39
  32. adam/commands/cql/cql_completions.py +9 -4
  33. adam/commands/cql/cqlsh.py +10 -30
  34. adam/commands/cql/{cql_utils.py → utils_cql.py} +149 -15
  35. adam/commands/deploy/code_start.py +7 -10
  36. adam/commands/deploy/code_stop.py +4 -21
  37. adam/commands/deploy/code_utils.py +3 -3
  38. adam/commands/deploy/deploy.py +4 -27
  39. adam/commands/deploy/deploy_frontend.py +14 -17
  40. adam/commands/deploy/deploy_pg_agent.py +2 -5
  41. adam/commands/deploy/deploy_pod.py +64 -68
  42. adam/commands/deploy/undeploy.py +4 -27
  43. adam/commands/deploy/undeploy_frontend.py +4 -7
  44. adam/commands/deploy/undeploy_pg_agent.py +4 -7
  45. adam/commands/deploy/undeploy_pod.py +9 -12
  46. adam/commands/devices/device.py +93 -2
  47. adam/commands/devices/device_app.py +37 -10
  48. adam/commands/devices/device_auit_log.py +8 -2
  49. adam/commands/devices/device_cass.py +47 -7
  50. adam/commands/devices/device_export.py +2 -2
  51. adam/commands/devices/device_postgres.py +41 -6
  52. adam/commands/exit.py +1 -4
  53. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  54. adam/commands/export/clean_up_export_sessions.py +18 -7
  55. adam/commands/export/drop_export_database.py +15 -18
  56. adam/commands/export/drop_export_databases.py +6 -9
  57. adam/commands/export/export.py +8 -38
  58. adam/commands/export/export_databases.py +16 -12
  59. adam/commands/export/export_handlers.py +71 -0
  60. adam/commands/export/export_select.py +33 -24
  61. adam/commands/export/export_use.py +12 -15
  62. adam/commands/export/exporter.py +37 -48
  63. adam/commands/export/import_session.py +4 -32
  64. adam/commands/export/importer_athena.py +4 -7
  65. adam/commands/export/importer_sqlite.py +19 -27
  66. adam/commands/export/show_column_counts.py +13 -22
  67. adam/commands/export/show_export_databases.py +3 -6
  68. adam/commands/export/show_export_session.py +10 -13
  69. adam/commands/export/show_export_sessions.py +8 -11
  70. adam/commands/export/utils_export.py +24 -1
  71. adam/commands/intermediate_command.py +49 -0
  72. adam/commands/issues.py +11 -43
  73. adam/commands/kubectl.py +3 -6
  74. adam/commands/login.py +22 -24
  75. adam/commands/logs.py +3 -6
  76. adam/commands/ls.py +8 -9
  77. adam/commands/medusa/medusa.py +4 -22
  78. adam/commands/medusa/medusa_backup.py +20 -24
  79. adam/commands/medusa/medusa_restore.py +29 -33
  80. adam/commands/medusa/medusa_show_backupjobs.py +14 -18
  81. adam/commands/medusa/medusa_show_restorejobs.py +11 -18
  82. adam/commands/nodetool.py +6 -15
  83. adam/commands/param_get.py +11 -12
  84. adam/commands/param_set.py +9 -10
  85. adam/commands/postgres/postgres.py +29 -37
  86. adam/commands/postgres/postgres_context.py +47 -23
  87. adam/commands/postgres/postgres_ls.py +4 -8
  88. adam/commands/postgres/postgres_preview.py +5 -9
  89. adam/commands/postgres/psql_completions.py +1 -1
  90. adam/commands/postgres/utils_postgres.py +66 -0
  91. adam/commands/preview_table.py +5 -44
  92. adam/commands/pwd.py +13 -16
  93. adam/commands/reaper/reaper.py +4 -27
  94. adam/commands/reaper/reaper_forward.py +48 -55
  95. adam/commands/reaper/reaper_forward_session.py +6 -0
  96. adam/commands/reaper/reaper_forward_stop.py +10 -16
  97. adam/commands/reaper/reaper_restart.py +7 -14
  98. adam/commands/reaper/reaper_run_abort.py +11 -30
  99. adam/commands/reaper/reaper_runs.py +42 -57
  100. adam/commands/reaper/reaper_runs_abort.py +29 -49
  101. adam/commands/reaper/reaper_schedule_activate.py +11 -30
  102. adam/commands/reaper/reaper_schedule_start.py +10 -29
  103. adam/commands/reaper/reaper_schedule_stop.py +10 -29
  104. adam/commands/reaper/reaper_schedules.py +4 -14
  105. adam/commands/reaper/reaper_status.py +8 -16
  106. adam/commands/reaper/utils_reaper.py +196 -0
  107. adam/commands/repair/repair.py +4 -22
  108. adam/commands/repair/repair_log.py +5 -11
  109. adam/commands/repair/repair_run.py +27 -34
  110. adam/commands/repair/repair_scan.py +32 -38
  111. adam/commands/repair/repair_stop.py +5 -11
  112. adam/commands/report.py +27 -29
  113. adam/commands/restart.py +25 -26
  114. adam/commands/rollout.py +19 -24
  115. adam/commands/shell.py +10 -4
  116. adam/commands/show/show.py +10 -26
  117. adam/commands/show/show_cassandra_repairs.py +35 -0
  118. adam/commands/show/show_cassandra_status.py +32 -43
  119. adam/commands/show/show_cassandra_version.py +5 -18
  120. adam/commands/show/show_commands.py +19 -24
  121. adam/commands/show/show_host.py +1 -1
  122. adam/commands/show/show_login.py +20 -27
  123. adam/commands/show/show_processes.py +15 -19
  124. adam/commands/show/show_storage.py +10 -20
  125. adam/commands/watch.py +26 -29
  126. adam/config.py +4 -16
  127. adam/embedded_params.py +1 -1
  128. adam/log.py +4 -4
  129. adam/pod_exec_result.py +3 -3
  130. adam/repl.py +29 -32
  131. adam/repl_commands.py +11 -11
  132. adam/repl_state.py +52 -26
  133. adam/sql/sql_completer.py +4 -6
  134. adam/sql/sql_state_machine.py +21 -14
  135. adam/sso/authn_ad.py +6 -8
  136. adam/sso/authn_okta.py +4 -6
  137. adam/sso/cred_cache.py +3 -5
  138. adam/sso/idp.py +9 -12
  139. adam/utils.py +393 -33
  140. adam/utils_athena.py +14 -13
  141. adam/utils_audits.py +12 -12
  142. adam/utils_issues.py +32 -0
  143. adam/utils_k8s/app_clusters.py +13 -18
  144. adam/utils_k8s/app_pods.py +2 -0
  145. adam/utils_k8s/cassandra_clusters.py +21 -18
  146. adam/utils_k8s/custom_resources.py +16 -17
  147. adam/utils_k8s/ingresses.py +2 -2
  148. adam/utils_k8s/jobs.py +7 -11
  149. adam/utils_k8s/k8s.py +87 -0
  150. adam/utils_k8s/pods.py +14 -76
  151. adam/utils_k8s/secrets.py +4 -4
  152. adam/utils_k8s/service_accounts.py +5 -4
  153. adam/utils_k8s/services.py +2 -2
  154. adam/utils_k8s/statefulsets.py +1 -12
  155. adam/utils_repl/state_machine.py +3 -3
  156. adam/utils_sqlite.py +78 -42
  157. adam/version.py +1 -1
  158. {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/METADATA +1 -1
  159. kaqing-2.0.172.dist-info/RECORD +230 -0
  160. adam/commands/app.py +0 -67
  161. adam/commands/app_ping.py +0 -44
  162. adam/commands/export/clean_up_export_session.py +0 -53
  163. adam/commands/postgres/postgres_utils.py +0 -31
  164. adam/commands/reaper/reaper_session.py +0 -159
  165. adam/commands/show/show_app_actions.py +0 -56
  166. adam/commands/show/show_app_id.py +0 -47
  167. adam/commands/show/show_app_queues.py +0 -45
  168. adam/commands/show/show_repairs.py +0 -47
  169. kaqing-2.0.145.dist-info/RECORD +0 -227
  170. {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/WHEEL +0 -0
  171. {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/entry_points.txt +0 -0
  172. {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ from adam.repl_session import ReplSession
8
8
  from adam.utils_k8s.kube_context import KubeContext
9
9
  from adam.utils_k8s.pods import Pods
10
10
  from adam.utils_k8s.secrets import Secrets
11
- from adam.utils import log2
11
+ from adam.utils import log2, log_exc
12
12
 
13
13
  class PostgresContext:
14
14
  def apply(namespace: str, path: str, arg: str = None) -> 'PostgresContext':
@@ -149,36 +149,62 @@ class PostgresContext:
149
149
 
150
150
  return r
151
151
  else:
152
- ns = self.namespace
153
- pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
154
-
155
- if Config().get('pg.agent.just-in-time', False):
156
- if not PostgresContext.deploy_pg_agent(pod_name, ns):
157
- return
152
+ pod_name, container_name = PostgresContext.pod_and_container(self.namespace)
153
+ if not pod_name:
154
+ return
158
155
 
159
- real_pod_name = pod_name
160
- try:
161
- # try with dedicated pg agent pod name configured
162
- Pods.get(ns, pod_name)
163
- except:
164
- try:
165
- # try with the ops pod
166
- pod_name = Config().get('pod.name', 'ops')
167
- real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
168
- except:
169
- log2(f"Could not locate {pod_name} pod.")
170
- return None
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
171
175
 
172
176
  cmd = f'psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
173
177
  env_prefix = f'PGPASSWORD="{self.password()}"'
174
178
 
175
- r = Pods.exec(real_pod_name, pod_name, ns, cmd, show_out=show_out, background=background, env_prefix=env_prefix)
179
+ r = Pods.exec(pod_name, container_name, self.namespace, cmd, show_out=show_out, background=background, env_prefix=env_prefix)
176
180
  if r and Config().get('repl.history.push-cat-remote-log-file', True):
177
181
  if r.log_file and ReplSession().prompt_session:
178
182
  ReplSession().prompt_session.history.append_string(f'@{r.pod} cat {r.log_file}')
179
183
 
180
184
  return r
181
185
 
186
+ def pod_and_container(ns: str):
187
+ container_name = Config().get('pg.agent.name', 'ops-pg-agent')
188
+
189
+ if Config().get('pg.agent.just-in-time', False):
190
+ if not PostgresContext.deploy_pg_agent(container_name, ns):
191
+ return None
192
+
193
+ pod_name = container_name
194
+ try:
195
+ # try with dedicated pg agent pod name configured
196
+ Pods.get(ns, container_name)
197
+ except:
198
+ try:
199
+ # try with the ops pod
200
+ container_name = Config().get('pod.name', 'ops')
201
+ pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
202
+ except:
203
+ log2(f"Could not locate {container_name} pod.")
204
+ return None
205
+
206
+ return pod_name, container_name
207
+
182
208
  def deploy_pg_agent(pod_name: str, ns: str) -> str:
183
209
  image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
184
210
  timeout = Config().get('pg.agent.timeout', 3600)
@@ -187,11 +213,9 @@ class PostgresContext:
187
213
  except Exception as e:
188
214
  if e.status == 409:
189
215
  if Pods.completed(ns, pod_name):
190
- try:
216
+ with log_exc(lambda e2: "Exception when calling BatchV1Api->create_pod: %s\n" % e2):
191
217
  Pods.delete(pod_name, ns)
192
218
  Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
193
- except Exception as e2:
194
- log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
195
219
 
196
220
  return
197
221
  else:
@@ -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):
@@ -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,11 +1,6 @@
1
1
  from adam.commands.command import Command
2
- from adam.commands.cql.cql_utils import cassandra_table_names, run_cql
3
- from adam.commands.postgres.postgres_context import PostgresContext
4
- from adam.config import Config
2
+ from adam.commands.devices.devices import Devices
5
3
  from adam.repl_state import ReplState, RequiredState
6
- from adam.utils import lines_to_tabular, log, log2
7
- from adam.utils_athena import Athena
8
- from adam.utils_audits import Audits
9
4
 
10
5
  class PreviewTable(Command):
11
6
  COMMAND = 'preview'
@@ -29,47 +24,13 @@ class PreviewTable(Command):
29
24
  if not(args := self.args(cmd)):
30
25
  return super().run(cmd, state)
31
26
 
32
- state, args = self.apply_state(args, state)
33
- if not self.validate_state(state):
34
- return state
35
-
36
- if not args:
37
- def show_tables():
38
- if state.device == ReplState.P:
39
- pg = PostgresContext.apply(state.namespace, state.pg_path)
40
- lines = [db["name"] for db in pg.tables() if db["schema"] == PostgresContext.default_schema()]
41
- log(lines_to_tabular(lines, separator=','))
42
- elif state.device == ReplState.L:
43
- log(lines_to_tabular(Athena.table_names(), separator=','))
44
- else:
45
- log(lines_to_tabular(cassandra_table_names(state), separator=','))
46
-
47
- if state.in_repl:
48
- log2('Table is required.')
49
- log2()
50
- log2('Tables:')
51
- show_tables()
52
- else:
53
- log2('* Table is missing.')
54
- show_tables()
55
-
56
- Command.display_help()
27
+ with self.validate(args, state) as (args, state):
28
+ Devices.device(state).preview(args[0] if args else None, state)
57
29
 
58
- return 'command-missing'
59
-
60
- table = args[0]
61
-
62
- rows = Config().get('preview.rows', 10)
63
- if state.device == ReplState.P:
64
- PostgresContext.apply(state.namespace, state.pg_path).run_sql(f'select * from {table} limit {rows}')
65
- elif state.device == ReplState.L:
66
- Athena.run_query(f'select * from {table} limit {rows}')
67
- else:
68
- run_cql(state, f'select * from {table} limit {rows}', show_out=True, use_single_quotes=True, on_any=True)
69
-
70
- return state
30
+ return state
71
31
 
72
32
  def completion(self, _: ReplState):
33
+ # taken care of by the sql completer
73
34
  return {}
74
35
 
75
36
  def help(self, _: ReplState):
adam/commands/pwd.py CHANGED
@@ -2,7 +2,7 @@ from adam.app_session import AppSession
2
2
  from adam.commands.command import Command
3
3
  from adam.commands.devices.devices import Devices
4
4
  from adam.repl_state import ReplState
5
- from adam.utils import lines_to_tabular, log
5
+ from adam.utils import lines_to_tabular, log, log_exc
6
6
 
7
7
  class Pwd(Command):
8
8
  COMMAND = 'pwd'
@@ -23,23 +23,20 @@ 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)
26
+ with self.validate(args, state) as (_, state):
27
+ host = "unknown"
28
+ with log_exc():
29
+ app_session: AppSession = AppSession.create('c3', 'c3')
30
+ host = app_session.host
27
31
 
28
- host = "unknown"
29
- try:
30
- app_session: AppSession = AppSession.create('c3', 'c3')
31
- host = app_session.host
32
- except:
33
- pass
32
+ log(lines_to_tabular([device.pwd(state) for device in Devices.all()] + [
33
+ f'',
34
+ f'HOST\t{host}',
35
+ f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
36
+ ], 'DEVICE\tLOCATION', separator='\t'))
37
+ log()
34
38
 
35
- log(lines_to_tabular([device.pwd(state) for device in Devices.all()] + [
36
- f'',
37
- f'HOST\t{host}',
38
- f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
39
- ], 'DEVICE\tLOCATION', separator='\t'))
40
- log()
41
-
42
- return state
39
+ return state
43
40
 
44
41
  def completion(self, state: ReplState):
45
42
  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,35 +22,14 @@ class Reaper(Command):
24
22
 
25
23
  return cls.instance
26
24
 
27
- def __init__(self, successor: Command=None):
28
- super().__init__(successor)
29
-
30
25
  def command(self):
31
26
  return Reaper.COMMAND
32
27
 
33
- def required(self):
34
- return RequiredState.CLUSTER
35
-
36
- def run(self, cmd: str, state: ReplState):
37
- if not(args := self.args(cmd)):
38
- return super().run(cmd, state)
39
-
40
- if not self.validate_state(state):
41
- return state
42
-
43
- return super().intermediate_run(cmd, state, args, Reaper.cmd_list())
44
-
45
- def cmd_list():
28
+ def cmd_list(self):
46
29
  return [ReaperSchedules(), ReaperScheduleStop(), ReaperScheduleActivate(), ReaperScheduleStart(),
47
30
  ReaperForwardStop(), ReaperForward(), ReaperRunAbort(), ReaperRunsAbort(), ReaperRestart(),
48
31
  ReaperRuns(), ReaperStatus()]
49
32
 
50
- def completion(self, state: ReplState):
51
- if state.sts:
52
- return super().completion(state)
53
-
54
- return {}
55
-
56
33
  class ReaperCommandHelper(click.Command):
57
34
  def get_help(self, ctx: click.Context):
58
- Command.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper.cmd_list(), show_cluster_help=True)
35
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper().cmd_list(), show_cluster_help=True)
@@ -1,8 +1,10 @@
1
+ from functools import partial
1
2
  import threading
2
3
  import time
3
4
 
4
5
  from adam.commands.command import Command
5
- from .reaper_session import ReaperSession
6
+ from adam.commands.reaper.reaper_forward_session import ReaperForwardSession
7
+ from adam.commands.reaper.utils_reaper import Reapers, port_forwarding
6
8
  from adam.config import Config
7
9
  from adam.repl_session import ReplSession
8
10
  from adam.repl_state import ReplState, RequiredState
@@ -10,7 +12,6 @@ from adam.utils import lines_to_tabular, log2
10
12
 
11
13
  class ReaperForward(Command):
12
14
  COMMAND = 'reaper forward'
13
- reaper_login = None
14
15
 
15
16
  # the singleton pattern
16
17
  def __new__(cls, *args, **kwargs):
@@ -31,70 +32,62 @@ class ReaperForward(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
+ if not Reapers.pod_name(state):
37
+ return state
37
38
 
38
- if not(reaper := ReaperSession.create(state)):
39
- return state
39
+ spec = Reapers.reaper_spec(state)
40
+ if state.in_repl:
41
+ if ReaperForwardSession.is_forwarding:
42
+ log2("Another port-forward is already running.")
40
43
 
41
- spec = reaper.reaper_spec(state)
42
- if state.in_repl:
43
- if ReaperSession.is_forwarding:
44
- log2("Another port-forward is already running.")
45
-
46
- return "already-running"
47
-
48
- # make it a daemon to exit with a Ctrl-D
49
- thread = threading.Thread(target=self.loop, args=(state, reaper), daemon=True)
50
- thread.start()
51
-
52
- while not ReaperSession.is_forwarding:
53
- time.sleep(1)
54
-
55
- d = {
56
- 'reaper-ui': spec["web-uri"],
57
- 'reaper-username': spec["username"],
58
- 'reaper-password': spec["password"]
59
- }
60
- log2()
61
- log2(lines_to_tabular([f'{k},{v}' for k, v in d.items()], separator=','))
62
-
63
- for k, v in d.items():
64
- ReplSession().prompt_session.history.append_string(f'cp {k}')
65
- log2()
66
- log2(f'Use <Up> arrow key to copy the values to clipboard.')
67
- else:
68
- try:
69
- log2(f'Click: {spec["web-uri"]}')
70
- log2(f'username: {spec["username"]}')
71
- log2(f'password: {spec["password"]}')
44
+ return "already-running"
45
+
46
+ # make it a daemon to exit with a Ctrl-D
47
+ thread = threading.Thread(target=self.loop, args=(state,), daemon=True)
48
+ thread.start()
49
+
50
+ while not ReaperForwardSession.is_forwarding:
51
+ time.sleep(1)
52
+
53
+ d = {
54
+ 'reaper-ui': spec["web-uri"],
55
+ 'reaper-username': spec["username"],
56
+ 'reaper-password': spec["password"]
57
+ }
72
58
  log2()
73
- log2(f"Press Ctrl+C to break.")
59
+ log2(lines_to_tabular([f'{k},{v}' for k, v in d.items()], separator=','))
74
60
 
75
- time.sleep(Config().get('reaper.port-forward.timeout', 3600 * 24))
76
- except KeyboardInterrupt:
77
- pass
61
+ for k, v in d.items():
62
+ ReplSession().prompt_session.history.append_string(f'cp {k}')
63
+ log2()
64
+ log2(f'Use <Up> arrow key to copy the values to clipboard.')
65
+ else:
66
+ try:
67
+ log2(f'Click: {spec["web-uri"]}')
68
+ log2(f'username: {spec["username"]}')
69
+ log2(f'password: {spec["password"]}')
70
+ log2()
71
+ log2(f"Press Ctrl+C to break.")
72
+
73
+ time.sleep(Config().get('reaper.port-forward.timeout', 3600 * 24))
74
+ except KeyboardInterrupt:
75
+ pass
78
76
 
79
- return state
77
+ return state
80
78
 
81
- def loop(self, state: ReplState, reaper: ReaperSession):
82
- def body(uri: str, _: dict[str, str]):
83
- ReaperSession.is_forwarding = True
79
+ def loop(self, state: ReplState):
80
+ with port_forwarding(state, Reapers.local_port(), partial(Reapers.svc_or_pod, state), Reapers.target_port()):
81
+ ReaperForwardSession.is_forwarding = True
84
82
  try:
85
- while not ReaperSession.stopping.is_set():
83
+ while not ReaperForwardSession.stopping.is_set():
86
84
  time.sleep(1)
87
85
  finally:
88
- ReaperSession.stopping.clear()
89
- ReaperSession.is_forwarding = False
90
-
91
- return reaper.port_forwarded(state, 'webui', body)
86
+ ReaperForwardSession.stopping.clear()
87
+ ReaperForwardSession.is_forwarding = False
92
88
 
93
89
  def completion(self, state: ReplState):
94
- if state.sts:
95
- return super().completion(state)
96
-
97
- return {}
90
+ return super().completion(state)
98
91
 
99
92
  def help(self, _: ReplState):
100
93
  return f'{ReaperForward.COMMAND}\t port-forward to reaper'
@@ -0,0 +1,6 @@
1
+ import threading
2
+
3
+ class ReaperForwardSession:
4
+ is_forwarding = False
5
+ stopping = threading.Event()
6
+ schedules_ids_by_cluster: dict[str, list[str]] = {}
@@ -1,11 +1,11 @@
1
1
  from adam.commands.command import Command
2
- from .reaper_session import ReaperSession
2
+ from adam.commands.reaper.reaper_forward_session import ReaperForwardSession
3
+ from adam.commands.reaper.utils_reaper import Reapers
3
4
  from adam.repl_state import ReplState, RequiredState
4
5
  from adam.utils import log2
5
6
 
6
7
  class ReaperForwardStop(Command):
7
8
  COMMAND = 'reaper forward stop'
8
- reaper_login = None
9
9
 
10
10
  # the singleton pattern
11
11
  def __new__(cls, *args, **kwargs):
@@ -26,24 +26,18 @@ class ReaperForwardStop(Command):
26
26
  if not(args := self.args(cmd)):
27
27
  return super().run(cmd, state)
28
28
 
29
- state, args = self.apply_state(args, state)
30
- if not self.validate_state(state):
31
- return state
32
-
33
- if not ReaperSession.create(state):
34
- return state
29
+ with self.validate(args, state) as (args, state):
30
+ if not Reapers.pod_name(state):
31
+ return state
35
32
 
36
- ReaperSession.is_forwarding = False
37
- ReaperSession.stopping.set()
38
- log2("Stopped reaper forward session.")
33
+ ReaperForwardSession.is_forwarding = False
34
+ ReaperForwardSession.stopping.set()
35
+ log2("Stopped reaper forward session.")
39
36
 
40
- return state
37
+ return state
41
38
 
42
39
  def completion(self, state: ReplState):
43
- if state.sts:
44
- return super().completion(state)
45
-
46
- return {}
40
+ return super().completion(state)
47
41
 
48
42
  def help(self, _: ReplState):
49
43
  return f'{ReaperForwardStop.COMMAND}\t stop port-forward to reaper'
@@ -1,11 +1,10 @@
1
1
  from adam.commands.command import Command
2
+ from adam.commands.reaper.utils_reaper import Reapers
2
3
  from adam.utils_k8s.pods import Pods
3
- from .reaper_session import ReaperSession
4
4
  from adam.repl_state import ReplState, RequiredState
5
5
 
6
6
  class ReaperRestart(Command):
7
7
  COMMAND = 'reaper restart'
8
- reaper_login = None
9
8
 
10
9
  # the singleton pattern
11
10
  def __new__(cls, *args, **kwargs):
@@ -26,22 +25,16 @@ class ReaperRestart(Command):
26
25
  if not(args := self.args(cmd)):
27
26
  return super().run(cmd, state)
28
27
 
29
- state, args = self.apply_state(args, state)
30
- if not self.validate_state(state):
31
- return state
32
-
33
- if not(reaper := ReaperSession.create(state)):
34
- return state
28
+ with self.validate(args, state) as (args, state):
29
+ if not (pod := Reapers.pod_name(state)):
30
+ return state
35
31
 
36
- Pods.delete(reaper.pod, state.namespace)
32
+ Pods.delete(pod, state.namespace)
37
33
 
38
- return state
34
+ return state
39
35
 
40
36
  def completion(self, state: ReplState):
41
- if state.sts:
42
- return super().completion(state)
43
-
44
- return {}
37
+ return super().completion(state)
45
38
 
46
39
  def help(self, _: ReplState):
47
40
  return f'{ReaperRestart.COMMAND}\t restart reaper'