kaqing 2.0.102__py3-none-any.whl → 2.0.104__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 (46) hide show
  1. adam/batch.py +0 -14
  2. adam/commands/audit/audit.py +4 -8
  3. adam/commands/audit/audit_repair_tables.py +13 -35
  4. adam/commands/audit/audit_run.py +49 -0
  5. adam/commands/bash.py +60 -2
  6. adam/commands/cd.py +8 -8
  7. adam/commands/commands_utils.py +1 -2
  8. adam/commands/cql/cql_completions.py +4 -4
  9. adam/commands/cql/cql_utils.py +1 -1
  10. adam/commands/cql/cqlsh.py +1 -1
  11. adam/commands/deploy/deploy_pg_agent.py +2 -2
  12. adam/commands/deploy/undeploy_pg_agent.py +2 -2
  13. adam/commands/ls.py +12 -12
  14. adam/commands/nodetool.py +1 -1
  15. adam/commands/postgres/postgres.py +3 -3
  16. adam/commands/postgres/{postgres_session.py → postgres_context.py} +26 -27
  17. adam/commands/postgres/postgres_utils.py +5 -5
  18. adam/commands/postgres/psql_completions.py +1 -1
  19. adam/commands/preview_table.py +8 -27
  20. adam/commands/pwd.py +2 -2
  21. adam/embedded_params.py +1 -1
  22. adam/repl.py +3 -3
  23. adam/repl_commands.py +3 -6
  24. adam/repl_state.py +2 -2
  25. adam/sql/sql_completer.py +67 -76
  26. adam/sql/sql_state_machine.py +518 -0
  27. adam/sql/term_completer.py +3 -0
  28. adam/utils_athena.py +68 -1
  29. adam/utils_k8s/pods.py +2 -2
  30. adam/version.py +1 -1
  31. {kaqing-2.0.102.dist-info → kaqing-2.0.104.dist-info}/METADATA +1 -1
  32. {kaqing-2.0.102.dist-info → kaqing-2.0.104.dist-info}/RECORD +35 -44
  33. adam/commands/audit/audit_table_completer.py +0 -9
  34. adam/commands/cql/cql_table_completer.py +0 -8
  35. adam/commands/describe/__init__.py +0 -0
  36. adam/commands/describe/describe.py +0 -61
  37. adam/commands/describe/describe_keyspace.py +0 -58
  38. adam/commands/describe/describe_keyspaces.py +0 -46
  39. adam/commands/describe/describe_schema.py +0 -46
  40. adam/commands/describe/describe_table.py +0 -57
  41. adam/commands/describe/describe_tables.py +0 -46
  42. adam/commands/postgres/psql_table_completer.py +0 -11
  43. adam/sql/state_machine.py +0 -576
  44. {kaqing-2.0.102.dist-info → kaqing-2.0.104.dist-info}/WHEEL +0 -0
  45. {kaqing-2.0.102.dist-info → kaqing-2.0.104.dist-info}/entry_points.txt +0 -0
  46. {kaqing-2.0.102.dist-info → kaqing-2.0.104.dist-info}/top_level.txt +0 -0
adam/batch.py CHANGED
@@ -9,7 +9,6 @@ from adam.commands.command_helpers import ClusterCommandHelper, ClusterOrPodComm
9
9
  from adam.commands.cql.cqlsh import CqlCommandHelper, Cqlsh
10
10
  from adam.commands.deploy.deploy import Deploy, DeployCommandHelper
11
11
  from adam.commands.deploy.undeploy import Undeploy, UndeployCommandHelper
12
- from adam.commands.describe.describe import Describe, DescribeCommandHelper
13
12
  from adam.commands.issues import Issues
14
13
  from adam.commands.login import Login
15
14
  from adam.commands.logs import Logs
@@ -97,19 +96,6 @@ def deploy(kubeconfig: str, config: str, param: list[str], namespace: str, extra
97
96
  run_command(Deploy(), kubeconfig, config, param, None, namespace, None, extra_args)
98
97
 
99
98
 
100
- @cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=DescribeCommandHelper, help='Describe keyspace(s) or table(s).')
101
- @click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
102
- @click.option('--config', default='params.yaml', metavar='path', help='path to kaqing parameters file')
103
- @click.option('--param', '-v', multiple=True, metavar='<key>=<value>', help='parameter override')
104
- @click.option('--cluster', '-c', required=False, metavar='statefulset', help='Kubernetes statefulset name')
105
- @click.option('--namespace', '-n', required=False, metavar='namespace', help='Kubernetes namespace')
106
- @click.option('--pod', '-p', required=False, metavar='pod', help='Kubernetes pod name')
107
- @click.option('--all-nodes', '-a', is_flag=True, help='execute on all Cassandra nodes')
108
- @click.argument('extra_args', nargs=-1, metavar='<cluster|pod>', type=click.UNPROCESSED)
109
- def describe(kubeconfig: str, config: str, param: list[str], cluster: str, namespace: str, pod: str, all_nodes: bool, extra_args):
110
- run_command(Describe(), kubeconfig, config, param, cluster, namespace, pod, extra_args + ('&',) if all_nodes else extra_args)
111
-
112
-
113
99
  @cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=ClusterOrPodCommandHelper, help="Print Qing's issues.")
114
100
  @click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
115
101
  @click.option('--config', default='params.yaml', metavar='path', help='path to kaqing parameters file')
@@ -1,6 +1,7 @@
1
1
  import click
2
2
 
3
3
  from adam.commands.audit.audit_repair_tables import AuditRepairTables
4
+ from adam.commands.audit.audit_run import AuditRun
4
5
  from adam.commands.command import Command
5
6
  from adam.config import Config
6
7
  from adam.repl_state import ReplState
@@ -54,22 +55,17 @@ class Audit(Command):
54
55
  audit_column_names()
55
56
  audit_column_names(partition_cols_only=True)
56
57
 
57
- # def columns(_):
58
- # return audit_column_names()
59
-
60
- return super().completion(state) | SqlCompleter.completions(
58
+ return super().completion(state) | SqlCompleter(
61
59
  lambda: audit_table_names(),
62
60
  columns=lambda table: audit_column_names(),
63
61
  partition_columns=lambda table: audit_column_names(partition_cols_only=True),
64
62
  variant='athena'
65
- )
66
- # | {
67
- # 'desc': {table: None for table in audit_table_names()}}
63
+ ).completions_for_nesting()
68
64
 
69
65
  return {}
70
66
 
71
67
  def cmd_list():
72
- return [AuditRepairTables()]
68
+ return [AuditRepairTables(), AuditRun()]
73
69
 
74
70
  def help(self, _: ReplState):
75
71
  return f'[{Audit.COMMAND}] <sql-statements>\t run SQL queries on Authena audit database'
@@ -6,7 +6,7 @@ from adam.commands.command import Command
6
6
  from adam.config import Config
7
7
  from adam.repl_state import ReplState
8
8
  from adam.utils import log, log2
9
- from adam.utils_athena import audit_query, run_audit_query
9
+ from adam.utils_athena import AuditMeta, audit_query, get_meta, put_meta, run_audit_query
10
10
 
11
11
  class AuditRepairTables(Command):
12
12
  COMMAND = 'audit repair'
@@ -30,11 +30,12 @@ class AuditRepairTables(Command):
30
30
 
31
31
  state, args = self.apply_state(args, state)
32
32
 
33
- tables = Config().get('audit.tables', 'audit').split(',')
33
+ tables = Config().get('audit.athena.repair-partition-tables', 'audit').split(',')
34
34
  if args:
35
35
  tables = args
36
36
 
37
- self.repair(tables)
37
+ meta = get_meta()
38
+ self.repair(tables, meta)
38
39
 
39
40
  return state
40
41
 
@@ -52,43 +53,20 @@ class AuditRepairTables(Command):
52
53
  def auto_repair(self, hours: int):
53
54
  self.auto_repaired = True
54
55
 
55
- state, _, rs = audit_query(f'select * from meta')
56
- if state == 'SUCCEEDED':
57
- do_repair = True
58
- if len(rs) > 1:
59
- try:
60
- checked_in = float(rs[1]['Data'][0]['VarCharValue'])
61
- do_repair = checked_in + hours * 60 * 60 < time.time()
62
- except:
63
- pass
64
-
65
- if do_repair:
66
- tables = Config().get('audit.athena.tables', 'audit').split(',')
67
- self.repair(tables, show_sql=True)
68
- log2(f'Audit tables have been auto-repaired.')
69
-
70
- def repair(self, tables: list[str], show_sql = False):
56
+ meta = get_meta()
57
+ if meta.checked_in + hours * 60 * 60 < time.time():
58
+ tables = Config().get('audit.athena.repair-partition-tables', 'audit').split(',')
59
+ self.repair(tables, meta, show_sql=True)
60
+ log2(f'Audit tables have been auto-repaired.')
61
+
62
+ def repair(self, tables: list[str], meta: AuditMeta, show_sql = False):
71
63
  with concurrent.futures.ThreadPoolExecutor(max_workers=Config().get('audit.workers', 3)) as executor:
72
64
  for table in tables:
73
65
  if show_sql:
74
66
  log(f'MSCK REPAIR TABLE {table}')
75
67
 
76
68
  executor.submit(run_audit_query, f'MSCK REPAIR TABLE {table}', None,)
77
- executor.submit(self.check_in,)
69
+ executor.submit(put_meta, 'check-in', meta,)
78
70
 
79
71
  def help(self, _: ReplState):
80
- return f"{AuditRepairTables.COMMAND} \t run MSCK REPAIR command for new partition discovery"
81
-
82
- def check_in(self):
83
- payload = {
84
- 'action': 'check-in'
85
- }
86
- audit_endpoint = Config().get("audit.endpoint", "https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/")
87
- try:
88
- response = requests.post(audit_endpoint, json=payload, timeout=Config().get("audit.timeout", 10))
89
- if response.status_code in [200, 201]:
90
- Config().debug(response.text)
91
- else:
92
- log2(f"Error: {response.status_code} {response.text}")
93
- except requests.exceptions.Timeout as e:
94
- log2(f"Timeout occurred: {e}")
72
+ return f"{AuditRepairTables.COMMAND} \t run MSCK REPAIR command for new partition discovery"
@@ -0,0 +1,49 @@
1
+ from adam.commands.command import Command
2
+ from adam.config import Config
3
+ from adam.repl_state import ReplState
4
+ from adam.utils import log2
5
+ from adam.utils_athena import AuditMeta, find_new_clusters, get_meta, put_meta, run_audit_query
6
+
7
+ class AuditRun(Command):
8
+ COMMAND = 'audit run'
9
+
10
+ # the singleton pattern
11
+ def __new__(cls, *args, **kwargs):
12
+ if not hasattr(cls, 'instance'): cls.instance = super(AuditRun, cls).__new__(cls)
13
+
14
+ return cls.instance
15
+
16
+ def __init__(self, successor: Command=None):
17
+ super().__init__(successor)
18
+ self.auto_repaired = False
19
+
20
+ def command(self):
21
+ return AuditRun.COMMAND
22
+
23
+ def run(self, cmd: str, state: ReplState):
24
+ if not(args := self.args(cmd)):
25
+ return super().run(cmd, state)
26
+
27
+ state, args = self.apply_state(args, state)
28
+
29
+ meta: AuditMeta = get_meta()
30
+ clusters = find_new_clusters(meta.cluster_last_checked)
31
+ if clusters:
32
+ put_meta('add-clusters', meta, clusters=clusters)
33
+ log2(f'Added {len(clusters)} new clusters.')
34
+ tables = Config().get('audit.athena.repair-cluster-tables', 'cluster').split(',')
35
+ for table in tables:
36
+ run_audit_query(f'MSCK REPAIR TABLE {table}')
37
+ else:
38
+ log2(f'No new clusters were found.')
39
+
40
+ return state
41
+
42
+ def completion(self, state: ReplState):
43
+ if state.device == ReplState.L:
44
+ return super().completion(state)
45
+
46
+ return {}
47
+
48
+ def help(self, _: ReplState):
49
+ return f"{AuditRun.COMMAND} \t run"
adam/commands/bash.py CHANGED
@@ -3,6 +3,8 @@ from adam.utils_k8s.cassandra_clusters import CassandraClusters
3
3
  from adam.utils_k8s.cassandra_nodes import CassandraNodes
4
4
  from adam.pod_exec_result import PodExecResult
5
5
  from adam.repl_state import BashSession, ReplState, RequiredState
6
+ from adam.utils_repl.automata_completer import AutomataCompleter
7
+ from adam.utils_repl.state_machine import StateMachine
6
8
 
7
9
  class Bash(Command):
8
10
  COMMAND = 'bash'
@@ -84,9 +86,65 @@ class Bash(Command):
84
86
 
85
87
  def completion(self, state: ReplState):
86
88
  if state.pod or state.sts:
87
- return {Bash.COMMAND: None}
89
+ return {Bash.COMMAND: AutomataCompleter(BashStateMachine())}
88
90
 
89
91
  return {}
90
92
 
91
93
  def help(self, _: ReplState):
92
- return f'{Bash.COMMAND} [bash-commands]\t run bash on the Cassandra nodes'
94
+ return f'{Bash.COMMAND} [bash-commands]\t run bash on the Cassandra nodes'
95
+
96
+ BASH_SPEC = [
97
+ # <command> ::= <simple_command> | <pipeline> | <conditional_command>
98
+ # <simple_command> ::= <word> <argument>* <redirection>*
99
+ # <pipeline> ::= <command> '|' <command>
100
+ # <conditional_command> ::= <command> '&&' <command> | <command> '||' <command>
101
+ # <word> ::= <letter> <letter_or_digit>*
102
+ # <argument> ::= <word>
103
+ # <redirection> ::= '>' <filename> | '<' <filename>
104
+ # <filename> ::= <word>
105
+ # <letter> ::= 'a' | 'b' | ... | 'z' | 'A' | 'B' | ... | 'Z'
106
+ # <digit> ::= '0' | '1' | ... | '9'
107
+ # <letter_or_digit> ::= <letter> | <digit>
108
+
109
+ ' > word > word',
110
+ 'word > word > word ^ |,>,2>,<,&,&&,||',
111
+ '- > pipe > word_pipe',
112
+ '- > _rdr0_ > word_rdr0',
113
+ '- > _rdr1_ > word_rdr1',
114
+ '- > _rdr2_ > word_rdr2',
115
+ '- > & > word_bg ^ |,>,2>,<,&,&&,||',
116
+ '- > &&|_or_ > word',
117
+ 'word_a > word > word',
118
+ 'word_pipe > word > word',
119
+ 'word_rdr0 > word > word_rdr0_f',
120
+ 'word_rdr1 > word > word_rdr1_f',
121
+ 'word_rdr2 > word > word_rdr2_f',
122
+ 'word_rdr1_f > pipe > word_pipe ^ |,2>,<,&,&&,||',
123
+ '- > _rdr2_ > word_rdr2',
124
+ '- > _rdr0_ > word_rdr0',
125
+ 'word_rdr2_f > pipe > word_pipe ^ |,<,&,&&,||',
126
+ '- > _rdr0_ > word_rdr0',
127
+ '- > & > word_bg ^ |,>,2>,<,&,&&,||',
128
+ '- > &&|_or_ > word',
129
+ 'word_rdr0_f > pipe > word_pipe ^ |,&,&&,||',
130
+ '- > & > word_bg ^ |,>,2>,<,&,&&,||',
131
+ '- > &&|_or_ > word',
132
+ 'word_bg > &&|_or_ > ^ &&,||',
133
+ ]
134
+
135
+ BASH_KEYWORDS = [
136
+ '&',
137
+ '&&',
138
+ '|',
139
+ '||',
140
+ '>',
141
+ '2>',
142
+ '>>',
143
+ '<'
144
+ ]
145
+ class BashStateMachine(StateMachine[str]):
146
+ def spec(self) -> str:
147
+ return BASH_SPEC
148
+
149
+ def keywords(self) -> list[str]:
150
+ return BASH_KEYWORDS
adam/commands/cd.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from adam.commands.command import Command
2
2
  from adam.commands.postgres.postgres_utils import pg_database_names
3
- from adam.commands.postgres.postgres_session import PostgresSession
3
+ from adam.commands.postgres.postgres_context import PostgresContext
4
4
  from adam.utils_k8s.cassandra_clusters import CassandraClusters
5
5
  from adam.utils_k8s.kube_context import KubeContext
6
6
  from adam.utils_k8s.statefulsets import StatefulSets
@@ -36,11 +36,11 @@ class Cd(Command):
36
36
  if dir == '':
37
37
  state.pg_path = None
38
38
  else:
39
- session = PostgresSession(state.namespace, state.pg_path)
39
+ context: PostgresContext = PostgresContext.apply(state.namespace, state.pg_path, arg=dir)
40
40
  # patch up state.namespace from pg cd
41
- if not state.namespace and (ns := session.find_namespace(arg)):
42
- state.namespace = ns
43
- state.pg_path = session.directory(arg)
41
+ if not state.namespace and context.namespace:
42
+ state.namespace = context.namespace
43
+ state.pg_path = context.path()
44
44
  elif state.device == ReplState.A:
45
45
  if dir == '':
46
46
  state.app_env = None
@@ -91,13 +91,13 @@ class Cd(Command):
91
91
 
92
92
  def completion(self, state: ReplState):
93
93
  if state.device == ReplState.P:
94
- pg = PostgresSession(state.namespace, state.pg_path) if state.pg_path else None
94
+ pg: PostgresContext = PostgresContext.apply(state.namespace, state.pg_path) if state.pg_path else None
95
95
  if pg and pg.db:
96
96
  return {Cd.COMMAND: {'..': None}}
97
97
  elif pg and pg.host:
98
- return {Cd.COMMAND: {'..': None} | {p: None for p in pg_database_names(state.namespace, pg.directory())}}
98
+ return {Cd.COMMAND: {'..': None} | {p: None for p in pg_database_names(state.namespace, pg.path())}}
99
99
  else:
100
- return {Cd.COMMAND: {p: None for p in PostgresSession.hosts(state.namespace)}}
100
+ return {Cd.COMMAND: {p: None for p in PostgresContext.hosts(state.namespace)}}
101
101
  elif state.device == ReplState.A:
102
102
  if state.app_app:
103
103
  return {Cd.COMMAND: {'..': None}}
@@ -1,5 +1,4 @@
1
1
  from concurrent.futures import ThreadPoolExecutor
2
- import time
3
2
  from kubernetes import client
4
3
  from typing import List
5
4
 
@@ -10,7 +9,7 @@ from adam.utils_k8s.cassandra_nodes import CassandraNodes
10
9
  from adam.utils_k8s.pods import Pods
11
10
  from adam.utils_k8s.statefulsets import StatefulSets
12
11
  from adam.repl_state import ReplState
13
- from adam.utils import convert_seconds, duration, lines_to_tabular, log, log2
12
+ from adam.utils import duration, lines_to_tabular, log, log2
14
13
 
15
14
  def show_pods(pods: List[client.V1Pod], ns: str, show_namespace = True, show_host_id = True):
16
15
  if len(pods) == 0:
@@ -1,4 +1,4 @@
1
- from adam.commands.cql.cql_utils import table_names
1
+ from adam.commands.cql.cql_utils import cassandra_table_names
2
2
  from adam.config import Config
3
3
  from adam.repl_state import ReplState
4
4
  from adam.sql.sql_completer import SqlCompleter
@@ -8,8 +8,8 @@ def cql_completions(state: ReplState) -> dict[str, any]:
8
8
  return {
9
9
  'describe': {
10
10
  'keyspaces': None,
11
- 'table': {t: None for t in table_names(state)},
11
+ 'table': {t: None for t in cassandra_table_names(state)},
12
12
  'tables': None},
13
- } | SqlCompleter.completions(lambda: table_names(state), table_props=lambda: {
13
+ } | SqlCompleter(lambda: cassandra_table_names(state), table_props=lambda: {
14
14
  'GC_GRACE_SECONDS': ps
15
- }, variant='cql')
15
+ }, variant='cql').completions_for_nesting()
@@ -23,7 +23,7 @@ def keyspaces(state: ReplState, on_any=False):
23
23
 
24
24
  return parse_cql_desc_keyspaces(r.stdout if state.pod else r[0].stdout)
25
25
 
26
- def table_names(state: ReplState):
26
+ def cassandra_table_names(state: ReplState):
27
27
  return [f'{k}.{t}' for k, ts in tables(state, on_any=True).items() for t in ts]
28
28
 
29
29
  @functools.lru_cache()
@@ -3,7 +3,7 @@ import click
3
3
  from adam.commands.command import Command
4
4
  from adam.commands.command_helpers import ClusterOrPodCommandHelper
5
5
  from adam.commands.cql.cql_completions import cql_completions
6
- from .cql_utils import run_cql, table_names
6
+ from .cql_utils import run_cql, cassandra_table_names
7
7
  from adam.repl_state import ReplState, RequiredState
8
8
  from adam.utils import log, log2
9
9
 
@@ -1,5 +1,5 @@
1
1
  from adam.commands.command import Command
2
- from adam.commands.postgres.postgres_session import PostgresSession
2
+ from adam.commands.postgres.postgres_context import PostgresContext
3
3
  from adam.config import Config
4
4
  from adam.repl_state import ReplState, RequiredState
5
5
 
@@ -29,7 +29,7 @@ class DeployPgAgent(Command):
29
29
  if not self.validate_state(state):
30
30
  return state
31
31
 
32
- PostgresSession.deploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace)
32
+ PostgresContext.deploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace)
33
33
 
34
34
  def completion(self, state: ReplState):
35
35
  return super().completion(state)
@@ -1,6 +1,6 @@
1
1
  from adam.commands.command import Command
2
2
  from adam.commands.deploy.deploy_utils import deleting
3
- from adam.commands.postgres.postgres_session import PostgresSession
3
+ from adam.commands.postgres.postgres_context import PostgresContext
4
4
  from adam.config import Config
5
5
  from adam.repl_state import ReplState, RequiredState
6
6
 
@@ -30,7 +30,7 @@ class UndeployPgAgent(Command):
30
30
  if not self.validate_state(state):
31
31
  return state
32
32
 
33
- deleting('pod', lambda: PostgresSession.undeploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace))
33
+ deleting('pod', lambda: PostgresContext.undeploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace))
34
34
 
35
35
  return state
36
36
 
adam/commands/ls.py CHANGED
@@ -4,7 +4,7 @@ from adam.commands.command import Command
4
4
  from adam.commands.commands_utils import show_pods, show_rollout
5
5
  from adam.commands.cql.cqlsh import Cqlsh
6
6
  from adam.commands.postgres.postgres_utils import pg_database_names, pg_table_names
7
- from adam.commands.postgres.postgres_session import PostgresSession
7
+ from adam.commands.postgres.postgres_context import PostgresContext
8
8
  from adam.config import Config
9
9
  from adam.utils_k8s.custom_resources import CustomResources
10
10
  from adam.utils_k8s.ingresses import Ingresses
@@ -45,7 +45,7 @@ class Ls(Command):
45
45
 
46
46
  if state.device == ReplState.P:
47
47
  if state.pg_path:
48
- pg = PostgresSession(state.namespace, state.pg_path)
48
+ pg: PostgresContext = PostgresContext.apply(state.namespace, state.pg_path)
49
49
  if pg.db:
50
50
  self.show_pg_tables(pg)
51
51
  else:
@@ -110,25 +110,25 @@ class Ls(Command):
110
110
 
111
111
  def show_pg_hosts(self, state: ReplState):
112
112
  if state.namespace:
113
- def line(pg: PostgresSession):
114
- return f'{pg.directory()},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
113
+ def line(pg: PostgresContext):
114
+ return f'{pg.path()},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
115
115
 
116
- lines = [line(PostgresSession(state.namespace, pg)) for pg in PostgresSession.hosts(state.namespace)]
116
+ lines = [line(PostgresContext.apply(state.namespace, pg)) for pg in PostgresContext.hosts(state.namespace)]
117
117
 
118
118
  log(lines_to_tabular(lines, 'NAME,ENDPOINT,USERNAME,PASSWORD', separator=','))
119
119
  else:
120
- def line(pg: PostgresSession):
121
- return f'{pg.directory()},{pg.namespace},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
120
+ def line(pg: PostgresContext):
121
+ return f'{pg.path()},{pg.namespace},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
122
122
 
123
- lines = [line(PostgresSession(state.namespace, pg)) for pg in PostgresSession.hosts(state.namespace)]
123
+ lines = [line(PostgresContext.apply(state.namespace, pg)) for pg in PostgresContext.hosts(state.namespace)]
124
124
 
125
125
  log(lines_to_tabular(lines, 'NAME,NAMESPACE,ENDPOINT,USERNAME,PASSWORD', separator=','))
126
126
 
127
- def show_pg_databases(self, pg: PostgresSession):
128
- log(lines_to_tabular(pg_database_names(pg.namespace, pg.directory()), 'DATABASE', separator=','))
127
+ def show_pg_databases(self, pg: PostgresContext):
128
+ log(lines_to_tabular(pg_database_names(pg.namespace, pg.path()), 'DATABASE', separator=','))
129
129
 
130
- def show_pg_tables(self, pg: PostgresSession):
131
- log(lines_to_tabular(pg_table_names(pg.namespace, pg.directory()), 'NAME', separator=','))
130
+ def show_pg_tables(self, pg: PostgresContext):
131
+ log(lines_to_tabular(pg_table_names(pg.namespace, pg.path()), 'NAME', separator=','))
132
132
 
133
133
  def show_audit_log_tables(self):
134
134
  log(lines_to_tabular(audit_table_names(), 'NAME', separator=','))
adam/commands/nodetool.py CHANGED
@@ -47,7 +47,7 @@ class NodeTool(Command):
47
47
 
48
48
  def completion(self, state: ReplState):
49
49
  if state.pod or state.sts:
50
- return {NodeTool.COMMAND: {'help': None} | {c: None for c in NODETOOL_COMMANDS}}
50
+ return {NodeTool.COMMAND: {'help': None} | {c: {'&': None} for c in NODETOOL_COMMANDS}}
51
51
 
52
52
  return {}
53
53
 
@@ -5,7 +5,7 @@ from adam.commands.postgres.psql_completions import psql_completions
5
5
  from adam.commands.postgres.postgres_utils import pg_table_names
6
6
  from .postgres_ls import PostgresLs
7
7
  from .postgres_preview import PostgresPreview
8
- from .postgres_session import PostgresSession
8
+ from .postgres_context import PostgresContext
9
9
  from adam.repl_state import ReplState
10
10
  from adam.utils import log, log2
11
11
 
@@ -62,7 +62,7 @@ class Postgres(Command):
62
62
 
63
63
  return state
64
64
 
65
- PostgresSession(state.namespace, state.pg_path).run_sql(' '.join(args))
65
+ PostgresContext.apply(state.namespace, state.pg_path).run_sql(' '.join(args))
66
66
 
67
67
  def completion(self, state: ReplState):
68
68
  if state.device != state.P:
@@ -70,7 +70,7 @@ class Postgres(Command):
70
70
  return {}
71
71
 
72
72
  leaf = {}
73
- session = PostgresSession(state.namespace, state.pg_path)
73
+ session = PostgresContext.apply(state.namespace, state.pg_path)
74
74
  if session.db:
75
75
  if pg_table_names(state.namespace, state.pg_path):
76
76
  leaf = psql_completions(state.namespace, state.pg_path)
@@ -8,7 +8,28 @@ from adam.utils_k8s.pods import Pods
8
8
  from adam.utils_k8s.secrets import Secrets
9
9
  from adam.utils import log2
10
10
 
11
- class PostgresSession:
11
+ class PostgresContext:
12
+ def apply(namespace: str, path: str, arg: str = None) -> 'PostgresContext':
13
+ context = PostgresContext(namespace, path)
14
+
15
+ if arg:
16
+ if arg == '..':
17
+ if context.db:
18
+ context.db = None
19
+ else:
20
+ context.host = None
21
+ else:
22
+ tks = arg.split('@')
23
+ if not context.host:
24
+ context.host = tks[0]
25
+ else:
26
+ context.db = tks[0]
27
+
28
+ if not namespace and tks[1]:
29
+ context.namespace = tks[1]
30
+
31
+ return context
32
+
12
33
  def __init__(self, ns: str, path: str):
13
34
  self.namespace = ns
14
35
  self.conn_details = None
@@ -25,29 +46,7 @@ class PostgresSession:
25
46
  if len(tks) > 1:
26
47
  self.db = tks[1]
27
48
 
28
- def find_namespace(self, arg: str):
29
- if arg:
30
- tks = arg.split('@')
31
- if len(tks) > 1:
32
- return tks[1]
33
-
34
- return None
35
-
36
- def directory(self, arg: str = None):
37
- if arg:
38
- if arg == '..':
39
- if self.db:
40
- self.db = None
41
- else:
42
- self.host = None
43
- else:
44
- tks = arg.split('@')
45
- arg = tks[0]
46
- if not self.host:
47
- self.host = arg
48
- else:
49
- self.db = arg
50
-
49
+ def path(self):
51
50
  if not self.host:
52
51
  return None
53
52
 
@@ -58,7 +57,7 @@ class PostgresSession:
58
57
  return f'{self.host}/{self.db}'
59
58
 
60
59
  def hosts(ns: str):
61
- return PostgresSession.hosts_for_namespace(ns)
60
+ return PostgresContext.hosts_for_namespace(ns)
62
61
 
63
62
  @functools.lru_cache()
64
63
  def hosts_for_namespace(ns: str):
@@ -133,7 +132,7 @@ class PostgresSession:
133
132
  return dbs
134
133
 
135
134
  def run_sql(self, sql: str, show_out = True):
136
- db = self.db if self.db else PostgresSession.default_db()
135
+ db = self.db if self.db else PostgresContext.default_db()
137
136
 
138
137
  if KubeContext.in_cluster():
139
138
  cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
@@ -151,7 +150,7 @@ class PostgresSession:
151
150
  pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
152
151
 
153
152
  if Config().get('pg.agent.just-in-time', False):
154
- if not PostgresSession.deploy_pg_agent(pod_name, ns):
153
+ if not PostgresContext.deploy_pg_agent(pod_name, ns):
155
154
  return
156
155
 
157
156
  real_pod_name = pod_name
@@ -1,6 +1,6 @@
1
1
  import functools
2
2
 
3
- from adam.commands.postgres.postgres_session import PostgresSession
3
+ from adam.commands.postgres.postgres_context import PostgresContext
4
4
  from adam.config import Config
5
5
 
6
6
  TestPG = [False]
@@ -12,8 +12,8 @@ def pg_database_names(ns: str, pg_path: str):
12
12
 
13
13
  Config().wait_log('Inspecting Postgres Databases...')
14
14
 
15
- pg = PostgresSession(ns, pg_path)
16
- return [db['name'] for db in pg.databases() if db['owner'] == PostgresSession.default_owner()]
15
+ pg = PostgresContext.apply(ns, pg_path)
16
+ return [db['name'] for db in pg.databases() if db['owner'] == PostgresContext.default_owner()]
17
17
 
18
18
  @functools.lru_cache()
19
19
  def pg_table_names(ns: str, pg_path: str):
@@ -21,10 +21,10 @@ def pg_table_names(ns: str, pg_path: str):
21
21
  return ['C3_2_XYZ1']
22
22
 
23
23
  Config().wait_log('Inspecting Postgres Database...')
24
- return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresSession.default_schema()]
24
+ return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresContext.default_schema()]
25
25
 
26
26
  def pg_tables(ns: str, pg_path: str):
27
- pg = PostgresSession(ns, pg_path)
27
+ pg = PostgresContext.apply(ns, pg_path)
28
28
  if pg.db:
29
29
  return pg.tables()
30
30
 
@@ -7,4 +7,4 @@ def psql_completions(ns: str, pg_path: str):
7
7
  '\d': None,
8
8
  '\dt': None,
9
9
  '\du': None
10
- } | SqlCompleter.completions(lambda: pg_table_names(ns, pg_path))
10
+ } | SqlCompleter(lambda: pg_table_names(ns, pg_path)).completions_for_nesting()