kaqing 2.0.145__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 (209) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -12
  3. adam/apps.py +18 -4
  4. adam/batch.py +4 -4
  5. adam/checks/check_utils.py +16 -46
  6. adam/checks/cpu.py +7 -1
  7. adam/checks/cpu_metrics.py +52 -0
  8. adam/checks/disk.py +2 -3
  9. adam/columns/columns.py +3 -1
  10. adam/columns/cpu.py +3 -1
  11. adam/columns/cpu_metrics.py +22 -0
  12. adam/columns/memory.py +3 -4
  13. adam/commands/__init__.py +24 -0
  14. adam/commands/alter_tables.py +33 -48
  15. adam/commands/app/__init__.py +0 -0
  16. adam/commands/app/app.py +38 -0
  17. adam/commands/{app_ping.py → app/app_ping.py} +7 -13
  18. adam/commands/app/show_app_actions.py +49 -0
  19. adam/commands/{show → app}/show_app_id.py +8 -11
  20. adam/commands/{show → app}/show_app_queues.py +7 -14
  21. adam/commands/app/utils_app.py +106 -0
  22. adam/commands/audit/audit.py +21 -40
  23. adam/commands/audit/audit_repair_tables.py +14 -19
  24. adam/commands/audit/audit_run.py +14 -22
  25. adam/commands/audit/completions_l.py +15 -0
  26. adam/commands/audit/show_last10.py +4 -19
  27. adam/commands/audit/show_slow10.py +4 -18
  28. adam/commands/audit/show_top10.py +4 -16
  29. adam/commands/audit/utils_show_top10.py +15 -3
  30. adam/commands/bash/__init__.py +5 -0
  31. adam/commands/bash/bash.py +7 -104
  32. adam/commands/bash/utils_bash.py +16 -0
  33. adam/commands/cat.py +7 -27
  34. adam/commands/cd.py +7 -11
  35. adam/commands/check.py +15 -24
  36. adam/commands/cli_commands.py +8 -4
  37. adam/commands/clipboard_copy.py +87 -0
  38. adam/commands/code.py +21 -24
  39. adam/commands/command.py +207 -42
  40. adam/commands/commands_utils.py +25 -27
  41. adam/commands/cql/completions_c.py +28 -0
  42. adam/commands/cql/cqlsh.py +9 -33
  43. adam/commands/cql/{cql_utils.py → utils_cql.py} +111 -15
  44. adam/commands/deploy/code_start.py +7 -10
  45. adam/commands/deploy/code_stop.py +4 -21
  46. adam/commands/deploy/code_utils.py +3 -3
  47. adam/commands/deploy/deploy.py +4 -27
  48. adam/commands/deploy/deploy_frontend.py +14 -17
  49. adam/commands/deploy/deploy_pg_agent.py +3 -6
  50. adam/commands/deploy/deploy_pod.py +64 -68
  51. adam/commands/deploy/undeploy.py +4 -27
  52. adam/commands/deploy/undeploy_frontend.py +4 -7
  53. adam/commands/deploy/undeploy_pg_agent.py +5 -8
  54. adam/commands/deploy/undeploy_pod.py +9 -12
  55. adam/commands/devices/device.py +124 -2
  56. adam/commands/devices/device_app.py +41 -24
  57. adam/commands/devices/device_auit_log.py +10 -4
  58. adam/commands/devices/device_cass.py +48 -14
  59. adam/commands/devices/device_export.py +13 -12
  60. adam/commands/devices/device_postgres.py +105 -54
  61. adam/commands/download_file.py +47 -0
  62. adam/commands/exit.py +1 -4
  63. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  64. adam/commands/export/clean_up_export_sessions.py +9 -10
  65. adam/commands/export/completions_x.py +11 -0
  66. adam/commands/export/download_export_session.py +40 -0
  67. adam/commands/export/drop_export_database.py +7 -26
  68. adam/commands/export/drop_export_databases.py +5 -14
  69. adam/commands/export/export.py +6 -52
  70. adam/commands/export/export_databases.py +108 -32
  71. adam/commands/export/export_select.py +8 -59
  72. adam/commands/export/export_sessions.py +209 -0
  73. adam/commands/export/export_use.py +14 -20
  74. adam/commands/export/export_x_select.py +48 -0
  75. adam/commands/export/exporter.py +135 -167
  76. adam/commands/export/import_files.py +44 -0
  77. adam/commands/export/import_session.py +11 -35
  78. adam/commands/export/importer.py +19 -5
  79. adam/commands/export/importer_athena.py +112 -44
  80. adam/commands/export/importer_sqlite.py +42 -22
  81. adam/commands/export/show_column_counts.py +13 -31
  82. adam/commands/export/show_export_databases.py +7 -7
  83. adam/commands/export/show_export_session.py +8 -20
  84. adam/commands/export/show_export_sessions.py +6 -16
  85. adam/commands/export/utils_export.py +64 -11
  86. adam/commands/find_files.py +51 -0
  87. adam/commands/find_processes.py +76 -0
  88. adam/commands/head.py +36 -0
  89. adam/commands/help.py +2 -2
  90. adam/commands/intermediate_command.py +52 -0
  91. adam/commands/issues.py +11 -43
  92. adam/commands/kubectl.py +3 -6
  93. adam/commands/login.py +22 -24
  94. adam/commands/logs.py +3 -6
  95. adam/commands/ls.py +9 -10
  96. adam/commands/medusa/medusa.py +4 -22
  97. adam/commands/medusa/medusa_backup.py +20 -27
  98. adam/commands/medusa/medusa_restore.py +49 -46
  99. adam/commands/medusa/medusa_show_backupjobs.py +16 -18
  100. adam/commands/medusa/medusa_show_restorejobs.py +13 -18
  101. adam/commands/medusa/utils_medusa.py +15 -0
  102. adam/commands/nodetool.py +7 -21
  103. adam/commands/param_get.py +11 -14
  104. adam/commands/param_set.py +8 -12
  105. adam/commands/postgres/completions_p.py +22 -0
  106. adam/commands/postgres/postgres.py +34 -57
  107. adam/commands/postgres/postgres_databases.py +270 -0
  108. adam/commands/postgres/postgres_ls.py +4 -8
  109. adam/commands/postgres/postgres_preview.py +5 -9
  110. adam/commands/postgres/utils_postgres.py +79 -0
  111. adam/commands/preview_table.py +8 -45
  112. adam/commands/pwd.py +13 -16
  113. adam/commands/reaper/reaper.py +4 -27
  114. adam/commands/reaper/reaper_forward.py +49 -56
  115. adam/commands/reaper/reaper_forward_session.py +6 -0
  116. adam/commands/reaper/reaper_forward_stop.py +10 -16
  117. adam/commands/reaper/reaper_restart.py +7 -14
  118. adam/commands/reaper/reaper_run_abort.py +8 -33
  119. adam/commands/reaper/reaper_runs.py +43 -58
  120. adam/commands/reaper/reaper_runs_abort.py +29 -49
  121. adam/commands/reaper/reaper_schedule_activate.py +14 -33
  122. adam/commands/reaper/reaper_schedule_start.py +9 -33
  123. adam/commands/reaper/reaper_schedule_stop.py +9 -33
  124. adam/commands/reaper/reaper_schedules.py +4 -14
  125. adam/commands/reaper/reaper_status.py +8 -16
  126. adam/commands/reaper/utils_reaper.py +203 -0
  127. adam/commands/repair/repair.py +4 -22
  128. adam/commands/repair/repair_log.py +5 -11
  129. adam/commands/repair/repair_run.py +27 -34
  130. adam/commands/repair/repair_scan.py +32 -40
  131. adam/commands/repair/repair_stop.py +5 -12
  132. adam/commands/report.py +27 -29
  133. adam/commands/restart.py +25 -26
  134. adam/commands/rollout.py +19 -24
  135. adam/commands/shell.py +12 -4
  136. adam/commands/show/show.py +11 -27
  137. adam/commands/show/show_adam.py +3 -3
  138. adam/commands/show/show_cassandra_repairs.py +37 -0
  139. adam/commands/show/show_cassandra_status.py +47 -51
  140. adam/commands/show/show_cassandra_version.py +5 -18
  141. adam/commands/show/show_cli_commands.py +56 -0
  142. adam/commands/show/show_host.py +1 -1
  143. adam/commands/show/show_login.py +20 -27
  144. adam/commands/show/show_params.py +2 -5
  145. adam/commands/show/show_processes.py +18 -21
  146. adam/commands/show/show_storage.py +11 -20
  147. adam/commands/watch.py +26 -29
  148. adam/config.py +5 -16
  149. adam/embedded_params.py +1 -1
  150. adam/log.py +4 -4
  151. adam/pod_exec_result.py +3 -3
  152. adam/repl.py +45 -39
  153. adam/repl_commands.py +26 -19
  154. adam/repl_session.py +8 -1
  155. adam/repl_state.py +85 -36
  156. adam/sql/lark_completer.py +284 -0
  157. adam/sql/lark_parser.py +604 -0
  158. adam/sql/sql_completer.py +4 -6
  159. adam/sql/sql_state_machine.py +29 -16
  160. adam/sso/authn_ad.py +6 -8
  161. adam/sso/authn_okta.py +4 -6
  162. adam/sso/cred_cache.py +3 -5
  163. adam/sso/idp.py +9 -12
  164. adam/utils.py +484 -37
  165. adam/utils_athena.py +19 -19
  166. adam/utils_audits.py +12 -12
  167. adam/utils_issues.py +32 -0
  168. adam/utils_k8s/app_clusters.py +14 -19
  169. adam/utils_k8s/app_pods.py +7 -2
  170. adam/utils_k8s/cassandra_clusters.py +30 -19
  171. adam/utils_k8s/cassandra_nodes.py +2 -2
  172. adam/utils_k8s/custom_resources.py +16 -17
  173. adam/utils_k8s/ingresses.py +2 -2
  174. adam/utils_k8s/jobs.py +7 -11
  175. adam/utils_k8s/k8s.py +96 -0
  176. adam/utils_k8s/kube_context.py +2 -2
  177. adam/utils_k8s/pods.py +37 -81
  178. adam/utils_k8s/secrets.py +4 -4
  179. adam/utils_k8s/service_accounts.py +5 -4
  180. adam/utils_k8s/services.py +2 -2
  181. adam/utils_k8s/statefulsets.py +6 -14
  182. adam/utils_local.py +4 -0
  183. adam/utils_repl/appendable_completer.py +6 -0
  184. adam/utils_repl/repl_completer.py +128 -2
  185. adam/utils_repl/state_machine.py +3 -3
  186. adam/utils_sqlite.py +78 -42
  187. adam/version.py +1 -1
  188. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
  189. kaqing-2.0.189.dist-info/RECORD +253 -0
  190. kaqing-2.0.189.dist-info/top_level.txt +2 -0
  191. teddy/__init__.py +0 -0
  192. teddy/lark_parser.py +436 -0
  193. teddy/lark_parser2.py +618 -0
  194. adam/commands/app.py +0 -67
  195. adam/commands/cp.py +0 -95
  196. adam/commands/cql/cql_completions.py +0 -28
  197. adam/commands/export/clean_up_export_session.py +0 -53
  198. adam/commands/export/export_select_x.py +0 -54
  199. adam/commands/postgres/postgres_context.py +0 -248
  200. adam/commands/postgres/postgres_utils.py +0 -31
  201. adam/commands/postgres/psql_completions.py +0 -10
  202. adam/commands/reaper/reaper_session.py +0 -159
  203. adam/commands/show/show_app_actions.py +0 -56
  204. adam/commands/show/show_commands.py +0 -61
  205. adam/commands/show/show_repairs.py +0 -47
  206. kaqing-2.0.145.dist-info/RECORD +0 -227
  207. kaqing-2.0.145.dist-info/top_level.txt +0 -1
  208. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
  209. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
@@ -24,28 +24,11 @@ class CodeStop(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
- _, dry = Command.extract_options(args, '--dry')
32
- stop_user_codes(state.namespace, dry)
33
-
34
- # if not args:
35
- # log2('Please specify <port>.')
36
- # return state
27
+ with self.validate(args, state) as (args, state):
28
+ _, dry = Command.extract_options(args, '--dry')
29
+ stop_user_codes(state.namespace, dry)
37
30
 
38
- # port = args[0]
39
- # name = f'ops-{port}'
40
- # user = os.getenv("USER")
41
- # label_selector=f'user={user}'
42
- # Ingresses.delete_ingresses(state.namespace, label_selector=label_selector)
43
- # Services.delete_services(state.namespace, label_selector=label_selector)
44
-
45
- # pattern = f'/c3/c3/ops/code/{user}/'
46
- # self.kill_process_by_pattern(pattern)
47
-
48
- return state
31
+ return state
49
32
 
50
33
  def completion(self, state: ReplState):
51
34
  if state.namespace:
@@ -7,7 +7,7 @@ from adam.apps import Apps
7
7
  from adam.config import Config
8
8
  from adam.utils_k8s.ingresses import Ingresses
9
9
  from adam.utils_k8s.services import Services
10
- from adam.utils import log2, random_alphanumeric
10
+ from adam.utils import debug, log2, random_alphanumeric
11
11
 
12
12
  def start_user_code(namespace: str):
13
13
  try:
@@ -57,7 +57,7 @@ def kill_process_by_pattern(pattern, dry=False):
57
57
  pids = process.stdout.strip().split('\n')
58
58
 
59
59
  if not pids or pids == ['']:
60
- Config().debug(f"No processes found matching pattern: '{pattern}'")
60
+ debug(f"No processes found matching pattern: '{pattern}'")
61
61
  return
62
62
 
63
63
  for pid in pids:
@@ -66,7 +66,7 @@ def kill_process_by_pattern(pattern, dry=False):
66
66
  if not dry:
67
67
  subprocess.run(f"kill -9 {pid}", shell=True, check=True)
68
68
 
69
- Config().debug(f"Killed process with PID: {pid} (matching pattern: '{pattern}')")
69
+ debug(f"Killed process with PID: {pid} (matching pattern: '{pattern}')")
70
70
  except subprocess.CalledProcessError as e:
71
71
  log2(f"Error killing process {pid}: {e}")
72
72
 
@@ -1,14 +1,12 @@
1
1
  import click
2
2
 
3
- from adam.commands.command import Command
4
3
  from adam.commands.deploy.deploy_pg_agent import DeployPgAgent
5
4
  from adam.commands.deploy.deploy_pod import DeployPod
5
+ from adam.commands.intermediate_command import IntermediateCommand
6
6
  from .deploy_frontend import DeployFrontend
7
- from adam.repl_state import ReplState, RequiredState
8
7
 
9
- class Deploy(Command):
8
+ class Deploy(IntermediateCommand):
10
9
  COMMAND = 'deploy'
11
- reaper_login = None
12
10
 
13
11
  # the singleton pattern
14
12
  def __new__(cls, *args, **kwargs):
@@ -16,33 +14,12 @@ class Deploy(Command):
16
14
 
17
15
  return cls.instance
18
16
 
19
- def __init__(self, successor: Command=None):
20
- super().__init__(successor)
21
-
22
17
  def command(self):
23
18
  return Deploy.COMMAND
24
19
 
25
- def required(self):
26
- return RequiredState.NAMESPACE
27
-
28
- def run(self, cmd: str, state: ReplState):
29
- if not(args := self.args(cmd)):
30
- return super().run(cmd, state)
31
-
32
- if not self.validate_state(state):
33
- return state
34
-
35
- return super().intermediate_run(cmd, state, args, Deploy.cmd_list())
36
-
37
- def cmd_list():
20
+ def cmd_list(self):
38
21
  return [DeployFrontend(), DeployPod(), DeployPgAgent()]
39
22
 
40
- def completion(self, state: ReplState):
41
- if state.sts:
42
- return super().completion(state)
43
-
44
- return {}
45
-
46
23
  class DeployCommandHelper(click.Command):
47
24
  def get_help(self, ctx: click.Context):
48
- Command.intermediate_help(super().get_help(ctx), Deploy.COMMAND, Deploy.cmd_list())
25
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Deploy.COMMAND, Deploy().cmd_list())
@@ -26,24 +26,21 @@ class DeployFrontend(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
- log2('This will support c3/c3 only for demo.')
29
+ with self.validate(args, state) as (args, state):
30
+ log2('This will support c3/c3 only for demo.')
31
+
32
+ pod_name = Config().get('pod.name', 'ops')
33
+ label_selector = Config().get('pod.label-selector', 'run=ops')
34
+ try:
35
+ uri = deploy_frontend(pod_name, state.namespace, label_selector)
36
+ log2(f'Ops pod is available at {uri}.')
37
+ except Exception as e:
38
+ if e.status == 409:
39
+ log2(f"Error: '{pod_name}' already exists in namespace '{state.namespace}'.")
40
+ else:
41
+ log2(f"Error creating ingress or service: {e}")
34
42
 
35
- pod_name = Config().get('pod.name', 'ops')
36
- label_selector = Config().get('pod.label-selector', 'run=ops')
37
- try:
38
- uri = deploy_frontend(pod_name, state.namespace, label_selector)
39
- log2(f'Ops pod is available at {uri}.')
40
- except Exception as e:
41
- if e.status == 409:
42
- log2(f"Error: '{pod_name}' already exists in namespace '{state.namespace}'.")
43
- else:
44
- log2(f"Error creating ingress or service: {e}")
45
-
46
- return state
43
+ return state
47
44
 
48
45
  def completion(self, state: ReplState):
49
46
  return super().completion(state)
@@ -1,5 +1,5 @@
1
1
  from adam.commands.command import Command
2
- from adam.commands.postgres.postgres_context import PostgresContext
2
+ from adam.commands.postgres.postgres_databases import PostgresDatabases
3
3
  from adam.config import Config
4
4
  from adam.repl_state import ReplState, RequiredState
5
5
 
@@ -25,11 +25,8 @@ class DeployPgAgent(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
- PostgresContext.deploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace)
28
+ with self.validate(args, state) as (args, state):
29
+ PostgresDatabases.deploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace)
33
30
 
34
31
  def completion(self, state: ReplState):
35
32
  return super().completion(state)
@@ -1,5 +1,6 @@
1
1
  from kubernetes import client
2
2
 
3
+ from adam.commands import extract_options
3
4
  from adam.commands.command import Command
4
5
  from adam.commands.deploy.deploy_utils import deploy_frontend, gen_labels
5
6
  from adam.commands.deploy.undeploy_pod import UndeployPod
@@ -11,7 +12,7 @@ from adam.utils_k8s.pods import Pods
11
12
  from adam.utils_k8s.service_accounts import ServiceAccounts
12
13
  from adam.utils_k8s.volumes import ConfigMapMount
13
14
  from adam.repl_state import ReplState, RequiredState
14
- from adam.utils import ing, log2
15
+ from adam.utils import ing, log2, log_exc
15
16
 
16
17
  class DeployPod(Command):
17
18
  COMMAND = 'deploy pod'
@@ -35,73 +36,68 @@ class DeployPod(Command):
35
36
  if not(args := self.args(cmd)):
36
37
  return super().run(cmd, state)
37
38
 
38
- state, args = self.apply_state(args, state)
39
- if not self.validate_state(state):
40
- return state
41
-
42
- args, forced = Command.extract_options(args, '--force')
43
- if forced:
44
- UndeployPod().run(UndeployPod.COMMAND, state)
45
-
46
- if KubeContext.in_cluster():
47
- log2('This is doable only from outside of the Kubernetes cluster.')
48
- return state
49
-
50
- sa_name = Config().get('pod.sa.name', 'ops')
51
- sa_proto = Config().get('pod.sa.proto', 'c3')
52
- additional_cluster_roles = Config().get('pod.sa.additional-cluster-roles', 'c3aiops-k8ssandra-operator').split(',')
53
- label_selector = Config().get('pod.label-selector', 'run=ops')
54
- labels = gen_labels(label_selector)
55
-
56
- with ing('Creating service account'):
57
- ServiceAccounts.replicate(sa_name, state.namespace, sa_proto, labels=labels, add_cluster_roles=additional_cluster_roles)
58
-
59
- settings_filename = 'settings.yaml'
60
- settings_path = f'/kaqing/{settings_filename}'
61
- settings_data = None
62
- try:
63
- with open(settings_filename, 'r') as file:
64
- settings_data = file.read()
65
- except:
66
- try:
67
- with open(settings_path, 'r') as file:
68
- settings_data = file.read()
69
- except:
70
- pass
71
-
72
- if not settings_data:
73
- log2(f'{settings_filename} not found.')
74
- return state
75
-
76
- cm_name = Config().get('pod.cm.name', 'ops')
77
- map_data = {
78
- settings_filename : settings_data
79
- }
80
- with ing('Creating config map'):
81
- ConfigMaps.create(cm_name, state.namespace, map_data, labels=labels)
82
-
83
- pod_name = Config().get('pod.name', 'ops')
84
- image = Config().get('pod.image', 'seanahnsf/kaqing')
85
- security_context = client.V1SecurityContext(
86
- capabilities=client.V1Capabilities(
87
- add=["SYS_PTRACE"]
88
- )
89
- )
90
- with ing('Creating deployment'):
91
- Deployments.create(state.namespace,
92
- pod_name,
93
- image,
94
- env={'NAMESPACE': state.namespace},
95
- container_security_context=security_context,
96
- labels=labels,
97
- sa_name=sa_name,
98
- config_map_mount=ConfigMapMount(cm_name, settings_filename, settings_path))
99
-
100
- uri = deploy_frontend(pod_name, state.namespace, label_selector)
101
-
102
- Pods.wait_for_running(state.namespace, pod_name, msg=f'In moments, ops pod will be available at {uri}.', label_selector=label_selector)
103
-
104
- return state
39
+ with self.validate(args, state) as (args, state):
40
+ with extract_options(args, '--force') as (args, forced):
41
+ if forced:
42
+ UndeployPod().run(UndeployPod.COMMAND, state)
43
+
44
+ if KubeContext.in_cluster():
45
+ log2('This is doable only from outside of the Kubernetes cluster.')
46
+ return state
47
+
48
+ sa_name = Config().get('pod.sa.name', 'ops')
49
+ sa_proto = Config().get('pod.sa.proto', 'c3')
50
+ additional_cluster_roles = Config().get('pod.sa.additional-cluster-roles', 'c3aiops-k8ssandra-operator').split(',')
51
+ label_selector = Config().get('pod.label-selector', 'run=ops')
52
+ labels = gen_labels(label_selector)
53
+
54
+ with ing('Creating service account'):
55
+ ServiceAccounts.replicate(sa_name, state.namespace, sa_proto, labels=labels, add_cluster_roles=additional_cluster_roles)
56
+
57
+ settings_filename = 'settings.yaml'
58
+ settings_path = f'/kaqing/{settings_filename}'
59
+ settings_data = None
60
+ try:
61
+ with open(settings_filename, 'r') as file:
62
+ settings_data = file.read()
63
+ except:
64
+ with log_exc():
65
+ with open(settings_path, 'r') as file:
66
+ settings_data = file.read()
67
+
68
+ if not settings_data:
69
+ log2(f'{settings_filename} not found.')
70
+ return state
71
+
72
+ cm_name = Config().get('pod.cm.name', 'ops')
73
+ map_data = {
74
+ settings_filename : settings_data
75
+ }
76
+ with ing('Creating config map'):
77
+ ConfigMaps.create(cm_name, state.namespace, map_data, labels=labels)
78
+
79
+ pod_name = Config().get('pod.name', 'ops')
80
+ image = Config().get('pod.image', 'seanahnsf/kaqing')
81
+ security_context = client.V1SecurityContext(
82
+ capabilities=client.V1Capabilities(
83
+ add=["SYS_PTRACE"]
84
+ )
85
+ )
86
+ with ing('Creating deployment'):
87
+ Deployments.create(state.namespace,
88
+ pod_name,
89
+ image,
90
+ env={'NAMESPACE': state.namespace},
91
+ container_security_context=security_context,
92
+ labels=labels,
93
+ sa_name=sa_name,
94
+ config_map_mount=ConfigMapMount(cm_name, settings_filename, settings_path))
95
+
96
+ uri = deploy_frontend(pod_name, state.namespace, label_selector)
97
+
98
+ Pods.wait_for_running(state.namespace, pod_name, msg=f'In moments, ops pod will be available at {uri}.', label_selector=label_selector)
99
+
100
+ return state
105
101
 
106
102
  def completion(self, state: ReplState):
107
103
  return super().completion(state, {'--force': None})
@@ -1,14 +1,12 @@
1
1
  import click
2
2
 
3
- from adam.commands.command import Command
4
3
  from adam.commands.deploy.undeploy_frontend import UndeployFrontend
5
4
  from adam.commands.deploy.undeploy_pg_agent import UndeployPgAgent
6
5
  from adam.commands.deploy.undeploy_pod import UndeployPod
7
- from adam.repl_state import ReplState, RequiredState
6
+ from adam.commands.intermediate_command import IntermediateCommand
8
7
 
9
- class Undeploy(Command):
8
+ class Undeploy(IntermediateCommand):
10
9
  COMMAND = 'undeploy'
11
- reaper_login = None
12
10
 
13
11
  # the singleton pattern
14
12
  def __new__(cls, *args, **kwargs):
@@ -16,33 +14,12 @@ class Undeploy(Command):
16
14
 
17
15
  return cls.instance
18
16
 
19
- def __init__(self, successor: Command=None):
20
- super().__init__(successor)
21
-
22
17
  def command(self):
23
18
  return Undeploy.COMMAND
24
19
 
25
- def required(self):
26
- return RequiredState.NAMESPACE
27
-
28
- def run(self, cmd: str, state: ReplState):
29
- if not(args := self.args(cmd)):
30
- return super().run(cmd, state)
31
-
32
- if not self.validate_state(state):
33
- return state
34
-
35
- return super().intermediate_run(cmd, state, args, Undeploy.cmd_list())
36
-
37
- def cmd_list():
20
+ def cmd_list(self):
38
21
  return [UndeployFrontend(), UndeployPod(), UndeployPgAgent()]
39
22
 
40
- def completion(self, state: ReplState):
41
- if state.sts:
42
- return super().completion(state)
43
-
44
- return {}
45
-
46
23
  class UndeployCommandHelper(click.Command):
47
24
  def get_help(self, ctx: click.Context):
48
- Command.intermediate_help(super().get_help(ctx), Undeploy.COMMAND, Undeploy.cmd_list())
25
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Undeploy.COMMAND, Undeploy().cmd_list())
@@ -25,14 +25,11 @@ class UndeployFrontend(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
- label_selector = Config().get('pod.label-selector', 'run=ops')
33
- undeploy_frontend(state.namespace, label_selector)
28
+ with self.validate(args, state) as (args, state):
29
+ label_selector = Config().get('pod.label-selector', 'run=ops')
30
+ undeploy_frontend(state.namespace, label_selector)
34
31
 
35
- return state
32
+ return state
36
33
 
37
34
  def completion(self, state: ReplState):
38
35
  return super().completion(state)
@@ -1,5 +1,5 @@
1
1
  from adam.commands.command import Command
2
- from adam.commands.postgres.postgres_context import PostgresContext
2
+ from adam.commands.postgres.postgres_databases import PostgresDatabases
3
3
  from adam.config import Config
4
4
  from adam.repl_state import ReplState, RequiredState
5
5
  from adam.utils import ing
@@ -26,14 +26,11 @@ class UndeployPgAgent(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
- with ing('Deleting pod'):
34
- PostgresContext.undeploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace)
29
+ with self.validate(args, state) as (args, state):
30
+ with ing('Deleting pod'):
31
+ PostgresDatabases.undeploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace)
35
32
 
36
- return state
33
+ return state
37
34
 
38
35
  def completion(self, state: ReplState):
39
36
  return super().completion(state)
@@ -30,19 +30,16 @@ class UndeployPod(Command):
30
30
  if not(args := self.args(cmd)):
31
31
  return super().run(cmd, state)
32
32
 
33
- state, args = self.apply_state(args, state)
34
- if not self.validate_state(state):
35
- return state
36
-
37
- label_selector = Config().get('pod.label-selector', 'run=ops')
38
- with ing('Deleting service account'):
39
- ServiceAccounts.delete(state.namespace, label_selector=label_selector)
40
- with ing('Deleting config map'): ConfigMaps.delete_with_selector(state.namespace, label_selector)
41
- with ing('Deleting deployment'): Deployments.delete_with_selector(state.namespace, label_selector, grace_period_seconds=0)
42
- with ing('Deleting pod'): Pods.delete_with_selector(state.namespace, label_selector, grace_period_seconds=0)
43
- undeploy_frontend(state.namespace, label_selector)
33
+ with self.validate(args, state) as (args, state):
34
+ label_selector = Config().get('pod.label-selector', 'run=ops')
35
+ with ing('Deleting service account'):
36
+ ServiceAccounts.delete(state.namespace, label_selector=label_selector)
37
+ with ing('Deleting config map'): ConfigMaps.delete_with_selector(state.namespace, label_selector)
38
+ with ing('Deleting deployment'): Deployments.delete_with_selector(state.namespace, label_selector, grace_period_seconds=0)
39
+ with ing('Deleting pod'): Pods.delete_with_selector(state.namespace, label_selector, grace_period_seconds=0)
40
+ undeploy_frontend(state.namespace, label_selector)
44
41
 
45
- return state
42
+ return state
46
43
 
47
44
  def completion(self, state: ReplState):
48
45
  return super().completion(state)
@@ -1,8 +1,34 @@
1
1
  from abc import abstractmethod
2
+
2
3
  from adam.commands.command import Command
3
- from adam.repl_state import ReplState
4
+ from adam.config import Config
5
+ from adam.pod_exec_result import PodExecResult
6
+ from adam.repl_state import BashSession, ReplState
7
+ from adam.utils import log2
8
+ from adam.utils_k8s.pods import Pods
4
9
 
5
10
  class Device:
11
+ def pods(self, state: ReplState, me: str = None) -> tuple[list[str], str]:
12
+ return self.pod_names(state), me if me else self.pod(state)
13
+
14
+ def default_pod(self, state: ReplState) -> tuple[list[str], str]:
15
+ if me := self.pod(state):
16
+ return me
17
+
18
+ if pods := self.pod_names(state):
19
+ return pods[0]
20
+
21
+ return None
22
+
23
+ def pod(self, state: ReplState) -> str:
24
+ return None
25
+
26
+ def pod_names(self, state: ReplState) -> list[str]:
27
+ return []
28
+
29
+ def default_container(self, state: ReplState) -> str:
30
+ return None
31
+
6
32
  @abstractmethod
7
33
  def ls(self, cmd: str, state: ReplState):
8
34
  pass
@@ -16,6 +42,9 @@ class Device:
16
42
  def cd_completion(self, cmd: str, state: ReplState, default: dict = {}):
17
43
  return default
18
44
 
45
+ def direct_dirs(self, cmd: str, state: ReplState, default: dict = {}) -> list[str]:
46
+ return []
47
+
19
48
  @abstractmethod
20
49
  def pwd(self, state: ReplState):
21
50
  pass
@@ -23,5 +52,98 @@ class Device:
23
52
  def try_fallback_action(self, chain: Command, state: ReplState, cmd: str):
24
53
  return False, None
25
54
 
26
- def enter(state: ReplState):
55
+ def enter(self, state: ReplState):
56
+ pass
57
+
58
+ def preview(self, table: str, state: ReplState):
59
+ if not table:
60
+ if state.in_repl:
61
+ log2('Table is required.')
62
+ log2()
63
+ log2('Tables:')
64
+ self.show_tables(state)
65
+ else:
66
+ log2('* Table is missing.')
67
+ self.show_tables(state)
68
+
69
+ Command.display_help()
70
+
71
+ return 'command-missing'
72
+
73
+ rows = Config().get('preview.rows', 10)
74
+ self.show_table_preview(state, table, rows)
75
+
76
+ return state
77
+
78
+ @abstractmethod
79
+ def show_tables(self, state: ReplState):
27
80
  pass
81
+
82
+ @abstractmethod
83
+ def show_table_preview(self, state: ReplState, table: str, rows: int):
84
+ pass
85
+
86
+ def bash(self, s0: ReplState, s1: ReplState, args: list[str]):
87
+ if s1.in_repl:
88
+ if self.bash_target_changed(s0, s1):
89
+ r = self._exec_with_dir(s1, args)
90
+ else:
91
+ r = self._exec_with_dir(s0, args)
92
+
93
+ if not r:
94
+ s1.exit_bash()
95
+
96
+ return 'inconsistent pwd'
97
+
98
+ return r
99
+ else:
100
+ self.exec_no_dir(' '.join(args), s1)
101
+
102
+ return s1
103
+
104
+ def _exec_with_dir(self, state: ReplState, args: list[str]) -> list[PodExecResult]:
105
+ session_just_created = False
106
+ if not args:
107
+ session_just_created = True
108
+ session = BashSession(state.device)
109
+ state.enter_bash(session)
110
+
111
+ if state.bash_session:
112
+ if args != ['pwd']:
113
+ if args:
114
+ args.append('&&')
115
+ args.extend(['pwd', '>', f'/tmp/.qing-{state.bash_session.session_id}'])
116
+
117
+ if not session_just_created:
118
+ if pwd := state.bash_session.pwd(state):
119
+ args = ['cd', pwd, '&&'] + args
120
+
121
+ return self.exec_with_dir(' '.join(args), session_just_created, state)
122
+
123
+ @abstractmethod
124
+ def bash_target_changed(self, s0: ReplState, s1: ReplState):
125
+ pass
126
+
127
+ @abstractmethod
128
+ def exec_no_dir(self, command: str, state: ReplState):
129
+ pass
130
+
131
+ @abstractmethod
132
+ def exec_with_dir(self, command: str, session_just_created: bool, state: ReplState):
133
+ pass
134
+
135
+ def bash_completion(self, cmd: str, state: ReplState, default: dict = {}):
136
+ return default
137
+
138
+ def files(self, state: ReplState):
139
+ r: PodExecResult = Pods.exec(self.default_pod(state), self.default_container(state), state.namespace, f'find -maxdepth 1 -type f', show_out=Config().is_debug(), shell='bash')
140
+
141
+ log_files = []
142
+ for line in r.stdout.split('\n'):
143
+ line = line.strip(' \r')
144
+ if line:
145
+ if line.startswith('./'):
146
+ line = line[2:]
147
+ log_files.append(line)
148
+
149
+ return log_files