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
@@ -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)
@@ -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
+ PostgresContext.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)
@@ -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
+ PostgresContext.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,6 +1,10 @@
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
4
8
 
5
9
  class Device:
6
10
  @abstractmethod
@@ -10,6 +14,13 @@ class Device:
10
14
  def ls_completion(self, cmd: str, state: ReplState, default: dict = {}):
11
15
  return default
12
16
 
17
+ @abstractmethod
18
+ def cat(self, cmd: str, state: ReplState):
19
+ pass
20
+
21
+ def cat_completion(self, cmd: str, state: ReplState, default: dict = {}):
22
+ return default
23
+
13
24
  def cd(self, dir: str, state: ReplState):
14
25
  pass
15
26
 
@@ -23,5 +34,85 @@ class Device:
23
34
  def try_fallback_action(self, chain: Command, state: ReplState, cmd: str):
24
35
  return False, None
25
36
 
26
- def enter(state: ReplState):
37
+ def enter(self, state: ReplState):
38
+ pass
39
+
40
+ def preview(self, table: str, state: ReplState):
41
+ if not table:
42
+ if state.in_repl:
43
+ log2('Table is required.')
44
+ log2()
45
+ log2('Tables:')
46
+ self.show_tables(state)
47
+ else:
48
+ log2('* Table is missing.')
49
+ self.show_tables(state)
50
+
51
+ Command.display_help()
52
+
53
+ return 'command-missing'
54
+
55
+ rows = Config().get('preview.rows', 10)
56
+ self.show_table_preview(state, table, rows)
57
+
58
+ return state
59
+
60
+ @abstractmethod
61
+ def show_tables(self, state: ReplState):
62
+ pass
63
+
64
+ @abstractmethod
65
+ def show_table_preview(self, state: ReplState, table: str, rows: int):
66
+ pass
67
+
68
+ def bash(self, s0: ReplState, s1: ReplState, args: list[str]):
69
+ if s1.in_repl:
70
+ if self.bash_target_changed(s0, s1):
71
+ r = self._exec_with_dir(s1, args)
72
+ else:
73
+ r = self._exec_with_dir(s0, args)
74
+
75
+ if not r:
76
+ s1.exit_bash()
77
+
78
+ return 'inconsistent pwd'
79
+
80
+ return r
81
+ else:
82
+ self.exec_no_dir(' '.join(args), s1)
83
+
84
+ return s1
85
+
86
+ def _exec_with_dir(self, state: ReplState, args: list[str]) -> list[PodExecResult]:
87
+ session_just_created = False
88
+ if not args:
89
+ session_just_created = True
90
+ session = BashSession(state.device)
91
+ state.enter_bash(session)
92
+
93
+ if state.bash_session:
94
+ if args != ['pwd']:
95
+ if args:
96
+ args.append('&&')
97
+ args.extend(['pwd', '>', f'/tmp/.qing-{state.bash_session.session_id}'])
98
+
99
+ if not session_just_created:
100
+ if pwd := state.bash_session.pwd(state):
101
+ args = ['cd', pwd, '&&'] + args
102
+
103
+ return self.exec_with_dir(' '.join(args), session_just_created, state)
104
+
105
+ @abstractmethod
106
+ def bash_target_changed(self, s0: ReplState, s1: ReplState):
107
+ pass
108
+
109
+ @abstractmethod
110
+ def exec_no_dir(self, command: str, state: ReplState):
27
111
  pass
112
+
113
+ @abstractmethod
114
+ def exec_with_dir(self, command: str, session_just_created: bool, state: ReplState):
115
+ pass
116
+
117
+ def bash_completion(self, cmd: str, state: ReplState, default: dict = {}):
118
+ return default
@@ -1,10 +1,11 @@
1
1
  from adam.apps import Apps
2
- from adam.commands.bash.bash import Bash
3
- from adam.commands.command import Command
2
+ from adam.commands import app
3
+ from adam.commands.bash.bash_completer import BashCompleter
4
+ from adam.commands.command import Command, InvalidState
4
5
  from adam.commands.devices.device import Device
5
6
  from adam.config import Config
6
7
  from adam.repl_state import ReplState
7
- from adam.utils import lines_to_tabular, log
8
+ from adam.utils import lines_to_tabular, log, wait_log
8
9
  from adam.utils_k8s.app_pods import AppPods
9
10
  from adam.utils_k8s.ingresses import Ingresses
10
11
 
@@ -39,7 +40,7 @@ class DeviceApp(Command, Device):
39
40
 
40
41
  def ls(self, cmd: str, state: ReplState):
41
42
  if state.app_pod:
42
- return Bash().run('bash ' + cmd, state)
43
+ return self.bash(state, state, cmd.split(' '))
43
44
  elif state.app_app:
44
45
  pods = AppPods.pod_names(state.namespace, state.app_env, state.app_app)
45
46
 
@@ -64,7 +65,6 @@ class DeviceApp(Command, Device):
64
65
 
65
66
  log(lines_to_tabular(svcs, 'ENV', separator=','))
66
67
 
67
-
68
68
  def ls_completion(self, cmd, state, default: dict = {}):
69
69
  if state.app_app:
70
70
  def pod_names():
@@ -74,6 +74,18 @@ class DeviceApp(Command, Device):
74
74
 
75
75
  return default
76
76
 
77
+ def cat(self, cmd: str, state: ReplState):
78
+ if state.app_pod:
79
+ return self.bash(state, state, cmd.split(' '))
80
+
81
+ return InvalidState()
82
+
83
+ def cat_completion(self, cmd: str, state: ReplState, default: dict = {}):
84
+ if state.app_app:
85
+ return {f'@{p}': {cmd: None} for p in AppPods.pod_names(state.namespace, state.app_env, state.app_app)}
86
+
87
+ return default
88
+
77
89
  def cd(self, dir: str, state: ReplState):
78
90
  if dir == '':
79
91
  state.app_env = None
@@ -134,13 +146,28 @@ class DeviceApp(Command, Device):
134
146
  if state.app_pod == '*':
135
147
  if (pods := AppPods.pod_names(state.namespace, ea[0], ea[1])):
136
148
  state.app_pod = pods[0]
137
- Config().wait_log(f'Moving to {state.app_env}/{state.app_app}/{state.app_pod}...')
149
+ wait_log(f'Moving to {state.app_env}/{state.app_app}/{state.app_pod}...')
138
150
  else:
139
- Config().wait_log(f'No pods found, moving to {state.app_env}/{state.app_app}...')
151
+ wait_log(f'No pods found, moving to {state.app_env}/{state.app_app}...')
140
152
  else:
141
- Config().wait_log(f'Moving to {state.app_env}/{state.app_app}/{state.app_pod}...')
153
+ wait_log(f'Moving to {state.app_env}/{state.app_app}/{state.app_pod}...')
142
154
  elif len(ea) > 1:
143
155
  state.app_app = ea[1]
144
- Config().wait_log(f'Moving to {state.app_env}/{state.app_app}...')
156
+ wait_log(f'Moving to {state.app_env}/{state.app_app}...')
145
157
  else:
146
- Config().wait_log(f'Moving to {state.app_env}...')
158
+ wait_log(f'Moving to {state.app_env}...')
159
+
160
+ def bash_target_changed(self, s0: ReplState, s1: ReplState):
161
+ return s0.app_env != s1.app_env or s0.app_app != s1.app_app or s0.app_pod != s1.app_pod
162
+
163
+ def exec_no_dir(self, command: str, state: ReplState):
164
+ with app(state) as pods:
165
+ return pods.exec(command)
166
+
167
+ def exec_with_dir(self, command: str, session_just_created: bool, state: ReplState):
168
+ with app(state) as pods:
169
+ return pods.exec(command, not session_just_created)
170
+
171
+ def bash_completion(self, cmd: str, state: ReplState, default: dict = {}):
172
+ return {cmd: BashCompleter(lambda: [])} | \
173
+ {f'@{p}': {cmd: BashCompleter(lambda: [])} for p in AppPods.pod_names(state.namespace, state.app_env, state.app_app)}
@@ -36,8 +36,14 @@ class DeviceAuditLog(Command, Device):
36
36
  def ls(self, cmd: str, _: ReplState):
37
37
  log(lines_to_tabular(Athena.table_names(), 'NAME', separator=','))
38
38
 
39
- def pwd(self, state: ReplState):
39
+ def pwd(self, _: ReplState):
40
40
  return '\t'.join([f'{ReplState.L}:>', '/'])
41
41
 
42
42
  def try_fallback_action(self, chain: Command, state: ReplState, cmd: str):
43
- return True, chain.run(f'audit {cmd}', state)
43
+ return True, chain.run(f'audit {cmd}', state)
44
+
45
+ def show_tables(self, _: ReplState):
46
+ log(lines_to_tabular(Athena.table_names(), separator=','))
47
+
48
+ def show_table_preview(self, _: ReplState, table: str, rows: int):
49
+ Athena.run_query(f'select * from {table} limit {rows}')