kaqing 2.0.98__py3-none-any.whl → 2.0.203__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.
- adam/__init__.py +0 -2
- adam/app_session.py +9 -12
- adam/apps.py +18 -4
- adam/batch.py +11 -25
- adam/checks/check_utils.py +16 -46
- adam/checks/cpu.py +7 -1
- adam/checks/cpu_metrics.py +52 -0
- adam/checks/disk.py +2 -3
- adam/columns/columns.py +3 -1
- adam/columns/cpu.py +3 -1
- adam/columns/cpu_metrics.py +22 -0
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +24 -0
- adam/commands/alter_tables.py +37 -63
- adam/commands/app/app.py +38 -0
- adam/commands/{app_ping.py → app/app_ping.py} +8 -14
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/{show → app}/show_app_id.py +8 -11
- adam/commands/{show → app}/show_app_queues.py +8 -14
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/audit.py +31 -35
- adam/commands/audit/audit_repair_tables.py +26 -48
- adam/commands/audit/audit_run.py +50 -0
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +36 -0
- adam/commands/audit/show_slow10.py +36 -0
- adam/commands/audit/show_top10.py +36 -0
- adam/commands/audit/utils_show_top10.py +71 -0
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +36 -0
- adam/commands/bash/bash_completer.py +93 -0
- adam/commands/bash/utils_bash.py +16 -0
- adam/commands/cassandra/__init__.py +0 -0
- adam/commands/cassandra/download_cassandra_log.py +45 -0
- adam/commands/cassandra/nodetool.py +64 -0
- adam/commands/cassandra/nodetool_commands.py +120 -0
- adam/commands/{restart.py → cassandra/restart_cluster.py} +12 -26
- adam/commands/cassandra/restart_node.py +51 -0
- adam/commands/cassandra/restart_nodes.py +47 -0
- adam/commands/cassandra/rollout.py +88 -0
- adam/commands/cat.py +36 -0
- adam/commands/cd.py +14 -92
- adam/commands/check.py +18 -21
- adam/commands/cli_commands.py +8 -4
- adam/commands/clipboard_copy.py +87 -0
- adam/commands/code.py +57 -0
- adam/commands/command.py +212 -39
- adam/commands/commands_utils.py +20 -28
- adam/commands/cql/alter_tables.py +66 -0
- adam/commands/cql/completions_c.py +29 -0
- adam/commands/cql/cqlsh.py +10 -29
- adam/commands/cql/utils_cql.py +305 -0
- adam/commands/debug/__init__.py +0 -0
- adam/commands/debug/debug.py +22 -0
- adam/commands/debug/debug_completes.py +35 -0
- adam/commands/debug/debug_timings.py +35 -0
- adam/commands/deploy/code_start.py +7 -10
- adam/commands/deploy/code_stop.py +4 -21
- adam/commands/deploy/code_utils.py +3 -3
- adam/commands/deploy/deploy.py +4 -21
- adam/commands/deploy/deploy_frontend.py +14 -17
- adam/commands/deploy/deploy_pg_agent.py +3 -6
- adam/commands/deploy/deploy_pod.py +65 -73
- adam/commands/deploy/deploy_utils.py +14 -24
- adam/commands/deploy/undeploy.py +4 -21
- adam/commands/deploy/undeploy_frontend.py +4 -7
- adam/commands/deploy/undeploy_pg_agent.py +6 -8
- adam/commands/deploy/undeploy_pod.py +11 -12
- adam/commands/devices/__init__.py +0 -0
- adam/commands/devices/device.py +149 -0
- adam/commands/devices/device_app.py +163 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +179 -0
- adam/commands/devices/device_export.py +87 -0
- adam/commands/devices/device_postgres.py +160 -0
- adam/commands/devices/devices.py +25 -0
- adam/commands/download_cassandra_log.py +45 -0
- adam/commands/download_file.py +47 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/__init__.py +0 -0
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +39 -0
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +39 -0
- adam/commands/export/drop_export_databases.py +37 -0
- adam/commands/export/export.py +37 -0
- adam/commands/export/export_databases.py +247 -0
- adam/commands/export/export_select.py +34 -0
- adam/commands/export/export_sessions.py +211 -0
- adam/commands/export/export_use.py +49 -0
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +361 -0
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +44 -0
- adam/commands/export/importer.py +82 -0
- adam/commands/export/importer_athena.py +150 -0
- adam/commands/export/importer_sqlite.py +69 -0
- adam/commands/export/show_column_counts.py +45 -0
- adam/commands/export/show_export_databases.py +39 -0
- adam/commands/export/show_export_session.py +39 -0
- adam/commands/export/show_export_sessions.py +37 -0
- adam/commands/export/utils_export.py +366 -0
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/generate_report.py +52 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +12 -8
- adam/commands/intermediate_command.py +52 -0
- adam/commands/issues.py +14 -40
- adam/commands/kubectl.py +38 -0
- adam/commands/login.py +26 -25
- adam/commands/ls.py +11 -116
- adam/commands/medusa/medusa.py +4 -22
- adam/commands/medusa/medusa_backup.py +20 -27
- adam/commands/medusa/medusa_restore.py +35 -48
- adam/commands/medusa/medusa_show_backupjobs.py +17 -18
- adam/commands/medusa/medusa_show_restorejobs.py +13 -18
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +8 -19
- adam/commands/os/__init__.py +0 -0
- adam/commands/os/cat.py +36 -0
- adam/commands/os/download_file.py +47 -0
- adam/commands/os/find_files.py +51 -0
- adam/commands/os/find_processes.py +76 -0
- adam/commands/os/head.py +36 -0
- adam/commands/os/shell.py +41 -0
- adam/commands/param_get.py +11 -14
- adam/commands/param_set.py +8 -12
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +47 -55
- adam/commands/postgres/postgres_databases.py +269 -0
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/utils_postgres.py +79 -0
- adam/commands/preview_table.py +10 -61
- adam/commands/pwd.py +14 -46
- adam/commands/reaper/reaper.py +4 -24
- adam/commands/reaper/reaper_forward.py +49 -56
- adam/commands/reaper/reaper_forward_session.py +6 -0
- adam/commands/reaper/reaper_forward_stop.py +10 -16
- adam/commands/reaper/reaper_restart.py +7 -14
- adam/commands/reaper/reaper_run_abort.py +8 -33
- adam/commands/reaper/reaper_runs.py +43 -58
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +14 -33
- adam/commands/reaper/reaper_schedule_start.py +9 -33
- adam/commands/reaper/reaper_schedule_stop.py +9 -33
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +203 -0
- adam/commands/repair/repair.py +4 -22
- adam/commands/repair/repair_log.py +5 -11
- adam/commands/repair/repair_run.py +27 -34
- adam/commands/repair/repair_scan.py +32 -40
- adam/commands/repair/repair_stop.py +5 -12
- adam/commands/restart_cluster.py +47 -0
- adam/commands/restart_node.py +51 -0
- adam/commands/restart_nodes.py +47 -0
- adam/commands/rollout.py +19 -24
- adam/commands/shell.py +12 -4
- adam/commands/show/show.py +10 -23
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +37 -0
- adam/commands/show/show_cassandra_status.py +47 -51
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_cli_commands.py +56 -0
- adam/commands/show/show_host.py +1 -1
- adam/commands/show/show_login.py +23 -27
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +18 -21
- adam/commands/show/show_storage.py +11 -20
- adam/commands/watch.py +26 -29
- adam/config.py +5 -15
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/repl.py +105 -133
- adam/repl_commands.py +68 -28
- adam/repl_session.py +9 -1
- adam/repl_state.py +300 -62
- adam/sql/async_executor.py +44 -0
- adam/sql/lark_completer.py +286 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/qingl.lark +1076 -0
- adam/sql/sql_completer.py +104 -64
- adam/sql/sql_state_machine.py +630 -0
- adam/sql/term_completer.py +3 -0
- adam/sso/authn_ad.py +6 -8
- adam/sso/authn_okta.py +4 -6
- adam/sso/cred_cache.py +3 -5
- adam/sso/idp.py +9 -12
- adam/utils.py +640 -10
- adam/utils_athena.py +140 -87
- adam/utils_audits.py +102 -0
- adam/utils_issues.py +32 -0
- adam/utils_k8s/app_clusters.py +28 -0
- adam/utils_k8s/app_pods.py +35 -0
- adam/utils_k8s/cassandra_clusters.py +34 -21
- adam/utils_k8s/cassandra_nodes.py +9 -6
- adam/utils_k8s/custom_resources.py +16 -17
- adam/utils_k8s/ingresses.py +2 -2
- adam/utils_k8s/jobs.py +7 -11
- adam/utils_k8s/k8s.py +96 -0
- adam/utils_k8s/kube_context.py +3 -6
- adam/{pod_exec_result.py → utils_k8s/pod_exec_result.py} +11 -5
- adam/utils_k8s/pods.py +146 -75
- adam/utils_k8s/secrets.py +4 -4
- adam/utils_k8s/service_accounts.py +5 -4
- adam/utils_k8s/services.py +2 -2
- adam/utils_k8s/statefulsets.py +6 -14
- adam/utils_local.py +42 -0
- adam/utils_net.py +4 -4
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +89 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +137 -0
- adam/version.py +1 -1
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/METADATA +1 -1
- kaqing-2.0.203.dist-info/RECORD +277 -0
- kaqing-2.0.203.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- adam/commands/app.py +0 -67
- adam/commands/bash.py +0 -92
- adam/commands/cp.py +0 -95
- adam/commands/cql/cql_completions.py +0 -11
- adam/commands/cql/cql_table_completer.py +0 -8
- adam/commands/cql/cql_utils.py +0 -115
- adam/commands/describe/describe.py +0 -47
- adam/commands/describe/describe_keyspace.py +0 -60
- adam/commands/describe/describe_keyspaces.py +0 -49
- adam/commands/describe/describe_schema.py +0 -49
- adam/commands/describe/describe_table.py +0 -60
- adam/commands/describe/describe_tables.py +0 -49
- adam/commands/devices.py +0 -118
- adam/commands/logs.py +0 -39
- adam/commands/postgres/postgres_session.py +0 -240
- adam/commands/postgres/postgres_utils.py +0 -31
- adam/commands/postgres/psql_completions.py +0 -10
- adam/commands/postgres/psql_table_completer.py +0 -11
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/report.py +0 -57
- adam/commands/show/show_app_actions.py +0 -53
- adam/commands/show/show_commands.py +0 -61
- adam/commands/show/show_repairs.py +0 -47
- adam/sql/state_machine.py +0 -460
- kaqing-2.0.98.dist-info/RECORD +0 -191
- kaqing-2.0.98.dist-info/top_level.txt +0 -1
- /adam/commands/{describe → app}/__init__.py +0 -0
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/WHEEL +0 -0
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import re
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
from adam.config import Config
|
|
6
|
+
from adam.repl_session import ReplSession
|
|
7
|
+
from adam.repl_state import ReplState
|
|
8
|
+
from adam.utils_k8s.kube_context import KubeContext
|
|
9
|
+
from adam.utils_k8s.pods import Pods
|
|
10
|
+
from adam.utils_k8s.secrets import Secrets
|
|
11
|
+
from adam.utils import log2, log_exc
|
|
12
|
+
|
|
13
|
+
class ConnectionDetails:
|
|
14
|
+
def __init__(self, state: ReplState, namespace: str, host: str):
|
|
15
|
+
self.state = state
|
|
16
|
+
self.namespace = namespace
|
|
17
|
+
self.host = host
|
|
18
|
+
|
|
19
|
+
def endpoint(self):
|
|
20
|
+
return PostgresDatabases._connection_property(self.state, 'pg.secret.endpoint-key', 'postgres-db-endpoint', host=self.host)
|
|
21
|
+
|
|
22
|
+
def port(self):
|
|
23
|
+
return PostgresDatabases._connection_property(self.state, 'pg.secret.port-key', 'postgres-db-port', host=self.host)
|
|
24
|
+
|
|
25
|
+
def username(self):
|
|
26
|
+
return PostgresDatabases._connection_property(self.state, 'pg.secret.username-key', 'postgres-admin-username', host=self.host)
|
|
27
|
+
|
|
28
|
+
def password(self):
|
|
29
|
+
return PostgresDatabases._connection_property(self.state, 'pg.secret.password-key', 'postgres-admin-password', host=self.host)
|
|
30
|
+
|
|
31
|
+
class PostgresDatabases:
|
|
32
|
+
def hosts(state: ReplState, namespace: str = None):
|
|
33
|
+
if not namespace:
|
|
34
|
+
namespace = state.namespace
|
|
35
|
+
|
|
36
|
+
return [ConnectionDetails(state, namespace, host) for host in PostgresDatabases.host_names(namespace)]
|
|
37
|
+
|
|
38
|
+
@functools.lru_cache()
|
|
39
|
+
def host_names(namespace: str):
|
|
40
|
+
ss = Secrets.list_secrets(namespace, name_pattern=Config().get('pg.name-pattern', '^{namespace}.*k8spg.*'))
|
|
41
|
+
|
|
42
|
+
def excludes(name: str):
|
|
43
|
+
exs = Config().get('pg.excludes', '.helm., -admin-secret')
|
|
44
|
+
if exs:
|
|
45
|
+
for ex in exs.split(','):
|
|
46
|
+
if ex.strip(' ') in name:
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
return [s for s in ss if not excludes(s)]
|
|
52
|
+
|
|
53
|
+
def databases(state: ReplState, default_owner = False):
|
|
54
|
+
dbs = []
|
|
55
|
+
# List of databases
|
|
56
|
+
# Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
|
|
57
|
+
# ---------------------------------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
|
|
58
|
+
# postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
|
|
59
|
+
# stgawsscpsr_c3_c3 | postgres | UTF8 | C | C | | libc |
|
|
60
|
+
# template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
|
|
61
|
+
# | | | | | | | postgres=CTc/postgres
|
|
62
|
+
# (48 rows)
|
|
63
|
+
if r := PostgresDatabases.run_sql(state, '\l', show_out=False):
|
|
64
|
+
s = 0
|
|
65
|
+
for line in r.stdout.split('\n'):
|
|
66
|
+
line: str = line.strip(' \r')
|
|
67
|
+
if s == 0:
|
|
68
|
+
if 'List of databases' in line:
|
|
69
|
+
s = 1
|
|
70
|
+
elif s == 1:
|
|
71
|
+
if 'Name' in line and 'Owner' in line and 'Encoding' in line:
|
|
72
|
+
s = 2
|
|
73
|
+
elif s == 2:
|
|
74
|
+
if line.startswith('---------'):
|
|
75
|
+
s = 3
|
|
76
|
+
elif s == 3:
|
|
77
|
+
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
78
|
+
if groups and groups[1] != '|':
|
|
79
|
+
dbs.append({'name': groups[1], 'owner': groups[2]})
|
|
80
|
+
|
|
81
|
+
if default_owner:
|
|
82
|
+
dbs = [db for db in dbs if db['owner'] == PostgresDatabases.default_owner()]
|
|
83
|
+
|
|
84
|
+
return dbs
|
|
85
|
+
|
|
86
|
+
def tables(state: ReplState, default_schema = False):
|
|
87
|
+
dbs = []
|
|
88
|
+
# List of relations
|
|
89
|
+
# Schema | Name | Type | Owner
|
|
90
|
+
# ----------+------------------------------------------------------------+-------+---------------
|
|
91
|
+
# postgres | c3_2_admin_aclpriv | table | postgres
|
|
92
|
+
# postgres | c3_2_admin_aclpriv_a | table | postgres
|
|
93
|
+
if r := PostgresDatabases.run_sql(state, '\dt', show_out=False):
|
|
94
|
+
s = 0
|
|
95
|
+
for line in r.stdout.split('\n'):
|
|
96
|
+
line: str = line.strip(' \r')
|
|
97
|
+
if s == 0:
|
|
98
|
+
if 'List of relations' in line:
|
|
99
|
+
s = 1
|
|
100
|
+
elif s == 1:
|
|
101
|
+
if 'Schema' in line and 'Name' in line and 'Type' in line:
|
|
102
|
+
s = 2
|
|
103
|
+
elif s == 2:
|
|
104
|
+
if line.startswith('---------'):
|
|
105
|
+
s = 3
|
|
106
|
+
elif s == 3:
|
|
107
|
+
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
108
|
+
if groups and groups[1] != '|':
|
|
109
|
+
dbs.append({'schema': groups[1], 'name': groups[2]})
|
|
110
|
+
|
|
111
|
+
if default_schema:
|
|
112
|
+
dbs = [db for db in dbs if db["schema"] == PostgresDatabases.default_schema()]
|
|
113
|
+
|
|
114
|
+
return dbs
|
|
115
|
+
|
|
116
|
+
def run_sql(state: ReplState, sql: str, database: str = None, show_out = True, backgrounded = False):
|
|
117
|
+
if not database:
|
|
118
|
+
database = PostgresDatabases.database(state)
|
|
119
|
+
if not database:
|
|
120
|
+
database = PostgresDatabases.default_db()
|
|
121
|
+
|
|
122
|
+
username = PostgresDatabases.username(state)
|
|
123
|
+
password = PostgresDatabases.password(state)
|
|
124
|
+
endpoint = PostgresDatabases.endpoint(state)
|
|
125
|
+
|
|
126
|
+
if KubeContext.in_cluster():
|
|
127
|
+
cmd1 = f'env PGPASSWORD={password} psql -h {endpoint} -p {PostgresDatabases.port()} -U {username} {database} --pset pager=off -c'
|
|
128
|
+
log2(f'{cmd1} "{sql}"')
|
|
129
|
+
# remove double quotes from the sql argument
|
|
130
|
+
cmd = cmd1.split(' ') + [sql]
|
|
131
|
+
|
|
132
|
+
r = subprocess.run(cmd, capture_output=not backgrounded, text=True)
|
|
133
|
+
if show_out:
|
|
134
|
+
log2(r.stdout)
|
|
135
|
+
log2(r.stderr)
|
|
136
|
+
|
|
137
|
+
return r
|
|
138
|
+
else:
|
|
139
|
+
pod_name, container_name = PostgresDatabases.pod_and_container(state.namespace)
|
|
140
|
+
if not pod_name:
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
cmd = f'psql -h {endpoint} -p {PostgresDatabases.port(state)} -U {username} {database} --pset pager=off -c "{sql}"'
|
|
144
|
+
env_prefix = f'PGPASSWORD="{password}"'
|
|
145
|
+
|
|
146
|
+
r = Pods.exec(pod_name, container_name, state.namespace, cmd, show_out=show_out, backgrounded=backgrounded, env_prefix=env_prefix)
|
|
147
|
+
if r and r.log_file:
|
|
148
|
+
ReplSession().append_history(f'@{r.pod} cat {r.log_file}')
|
|
149
|
+
|
|
150
|
+
return r
|
|
151
|
+
|
|
152
|
+
@functools.lru_cache()
|
|
153
|
+
def pod_and_container(namespace: str):
|
|
154
|
+
container_name = Config().get('pg.agent.name', 'ops-pg-agent')
|
|
155
|
+
|
|
156
|
+
if Config().get('pg.agent.just-in-time', False):
|
|
157
|
+
if not PostgresDatabases.deploy_pg_agent(container_name, namespace):
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
pod_name = container_name
|
|
161
|
+
try:
|
|
162
|
+
# try with dedicated pg agent pod name configured
|
|
163
|
+
Pods.get(namespace, container_name)
|
|
164
|
+
except:
|
|
165
|
+
try:
|
|
166
|
+
# try with the ops pod
|
|
167
|
+
container_name = Config().get('pod.name', 'ops')
|
|
168
|
+
pod_name = Pods.get_with_selector(namespace, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
|
|
169
|
+
except:
|
|
170
|
+
log2(f"Could not locate {container_name} pod.")
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
return pod_name, container_name
|
|
174
|
+
|
|
175
|
+
def deploy_pg_agent(pod_name: str, namespace: str) -> str:
|
|
176
|
+
image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
|
|
177
|
+
timeout = Config().get('pg.agent.timeout', 3600)
|
|
178
|
+
try:
|
|
179
|
+
Pods.create(namespace, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': namespace}, sa_name='c3')
|
|
180
|
+
except Exception as e:
|
|
181
|
+
if e.status == 409:
|
|
182
|
+
if Pods.completed(namespace, pod_name):
|
|
183
|
+
with log_exc(lambda e2: "Exception when calling BatchV1Api->create_pod: %s\n" % e2):
|
|
184
|
+
Pods.delete(pod_name, namespace)
|
|
185
|
+
Pods.create(namespace, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': namespace}, sa_name='c3')
|
|
186
|
+
|
|
187
|
+
return
|
|
188
|
+
else:
|
|
189
|
+
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
|
|
190
|
+
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
Pods.wait_for_running(namespace, pod_name)
|
|
194
|
+
|
|
195
|
+
return pod_name
|
|
196
|
+
|
|
197
|
+
def undeploy_pg_agent(pod_name: str, namespace: str):
|
|
198
|
+
Pods.delete(pod_name, namespace, grace_period_seconds=0)
|
|
199
|
+
|
|
200
|
+
def endpoint(state: ReplState):
|
|
201
|
+
return PostgresDatabases._connection_property(state, 'pg.secret.endpoint-key', 'postgres-db-endpoint')
|
|
202
|
+
|
|
203
|
+
def port(state: ReplState):
|
|
204
|
+
return PostgresDatabases._connection_property(state, 'pg.secret.port-key', 'postgres-db-port')
|
|
205
|
+
|
|
206
|
+
def username(state: ReplState):
|
|
207
|
+
return PostgresDatabases._connection_property(state, 'pg.secret.username-key', 'postgres-admin-username')
|
|
208
|
+
|
|
209
|
+
def password(state: ReplState):
|
|
210
|
+
return PostgresDatabases._connection_property(state, 'pg.secret.password-key', 'postgres-admin-password')
|
|
211
|
+
|
|
212
|
+
def _connection_property(state: ReplState, config_key: str, default: str, host: str = None, database: str = None):
|
|
213
|
+
with pg_path(state, host=host, database=database) as (host, _):
|
|
214
|
+
if not (conn := PostgresDatabases.conn_details(state.namespace, host)):
|
|
215
|
+
return ''
|
|
216
|
+
|
|
217
|
+
key = Config().get(config_key, default)
|
|
218
|
+
return conn[key] if key in conn else ''
|
|
219
|
+
|
|
220
|
+
def default_db():
|
|
221
|
+
return Config().get('pg.default-db', 'postgres')
|
|
222
|
+
|
|
223
|
+
def default_owner():
|
|
224
|
+
return Config().get('pg.default-owner', 'postgres')
|
|
225
|
+
|
|
226
|
+
def default_schema():
|
|
227
|
+
return Config().get('pg.default-schema', 'postgres')
|
|
228
|
+
|
|
229
|
+
def host(state: ReplState):
|
|
230
|
+
if not state.pg_path:
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
return state.pg_path.split('/')[0]
|
|
234
|
+
|
|
235
|
+
def database(state: ReplState):
|
|
236
|
+
if not state.pg_path:
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
tokens = state.pg_path.split('/')
|
|
240
|
+
if len(tokens) > 1:
|
|
241
|
+
return tokens[1]
|
|
242
|
+
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
@functools.lru_cache()
|
|
246
|
+
def conn_details(namespace: str, host: str):
|
|
247
|
+
return Secrets.get_data(namespace, host)
|
|
248
|
+
|
|
249
|
+
class PostgresPathHandler:
|
|
250
|
+
def __init__(self, state: ReplState, host: str = None, database: str = None):
|
|
251
|
+
self.state = state
|
|
252
|
+
self.host = host
|
|
253
|
+
self.database = database
|
|
254
|
+
|
|
255
|
+
def __enter__(self) -> tuple[str, str]:
|
|
256
|
+
if self.state and self.state.pg_path:
|
|
257
|
+
host_n_db = self.state.pg_path.split('/')
|
|
258
|
+
if not self.host:
|
|
259
|
+
self.host = host_n_db[0]
|
|
260
|
+
if not self.database and len(host_n_db) > 1:
|
|
261
|
+
self.database = host_n_db[1]
|
|
262
|
+
|
|
263
|
+
return self.host, self.database
|
|
264
|
+
|
|
265
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
266
|
+
return False
|
|
267
|
+
|
|
268
|
+
def pg_path(state: ReplState, host: str = None, database: str = None):
|
|
269
|
+
return PostgresPathHandler(state, host=host, database=database)
|
|
@@ -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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
30
|
+
Ls().run('ls', state)
|
|
35
31
|
|
|
36
|
-
|
|
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.
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
30
|
+
PreviewTable().run(f'preview {" ".join(args)}', state)
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
return state
|
|
37
33
|
|
|
38
34
|
def completion(self, state: ReplState):
|
|
39
35
|
if state.sts:
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
from adam.commands.postgres.postgres_databases import PostgresDatabases, pg_path
|
|
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
|
+
def direct_dirs(state: ReplState) -> list[str]:
|
|
11
|
+
with pg_path(state) as (host, database):
|
|
12
|
+
if database:
|
|
13
|
+
return ['..']
|
|
14
|
+
elif host:
|
|
15
|
+
return ['..'] + pg_database_names(state)
|
|
16
|
+
else:
|
|
17
|
+
return PostgresDatabases.host_names(state.namespace)
|
|
18
|
+
|
|
19
|
+
def pg_database_names(state: ReplState):
|
|
20
|
+
# cache on pg_path
|
|
21
|
+
return _pg_database_names(state, state.pg_path)
|
|
22
|
+
|
|
23
|
+
@functools.lru_cache()
|
|
24
|
+
def _pg_database_names(state: ReplState, pg_path: str):
|
|
25
|
+
if TestPG[0]:
|
|
26
|
+
return ['azops88_c3ai_c3']
|
|
27
|
+
|
|
28
|
+
wait_log('Inspecting Postgres Databases...')
|
|
29
|
+
|
|
30
|
+
return [db['name'] for db in PostgresDatabases.databases(state, default_owner=True)]
|
|
31
|
+
|
|
32
|
+
def pg_table_names(state: ReplState):
|
|
33
|
+
# cache on pg_path
|
|
34
|
+
return _pg_table_names(state, state.pg_path)
|
|
35
|
+
|
|
36
|
+
@functools.lru_cache()
|
|
37
|
+
def _pg_table_names(state: ReplState, pg_path: str):
|
|
38
|
+
if TestPG[0]:
|
|
39
|
+
return ['C3_2_XYZ1']
|
|
40
|
+
|
|
41
|
+
wait_log('Inspecting Postgres Database...')
|
|
42
|
+
return [table['name'] for table in PostgresDatabases.tables(state, default_schema=True)]
|
|
43
|
+
|
|
44
|
+
class PostgresPodService:
|
|
45
|
+
def __init__(self, handler: 'PostgresExecHandler'):
|
|
46
|
+
self.handler = handler
|
|
47
|
+
|
|
48
|
+
def exec(self, command: str, show_out=True):
|
|
49
|
+
state = self.handler.state
|
|
50
|
+
|
|
51
|
+
pod, container = PostgresDatabases.pod_and_container(state.namespace)
|
|
52
|
+
if not pod:
|
|
53
|
+
log2('Cannot locate postgres agent or ops pod.')
|
|
54
|
+
return state
|
|
55
|
+
|
|
56
|
+
return Pods.exec(pod, container, state.namespace, command, show_out=show_out)
|
|
57
|
+
|
|
58
|
+
def sql(self, args: list[str], backgrounded=False):
|
|
59
|
+
state = self.handler.state
|
|
60
|
+
|
|
61
|
+
query = args
|
|
62
|
+
if isinstance(args, list):
|
|
63
|
+
query = ' '.join(args)
|
|
64
|
+
|
|
65
|
+
PostgresDatabases.run_sql(state, query, backgrounded=backgrounded)
|
|
66
|
+
|
|
67
|
+
class PostgresExecHandler:
|
|
68
|
+
def __init__(self, state: ReplState, backgrounded=False):
|
|
69
|
+
self.state = state
|
|
70
|
+
self.backgrounded = backgrounded
|
|
71
|
+
|
|
72
|
+
def __enter__(self):
|
|
73
|
+
return PostgresPodService(self)
|
|
74
|
+
|
|
75
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def postgres(state: ReplState, backgrounded=False):
|
|
79
|
+
return PostgresExecHandler(state, backgrounded=backgrounded)
|
adam/commands/preview_table.py
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
from adam.commands import validate_args
|
|
3
2
|
from adam.commands.command import Command
|
|
4
|
-
from adam.commands.
|
|
5
|
-
from adam.commands.cql.cql_utils import run_cql, table_names, tables
|
|
6
|
-
from adam.commands.postgres.postgres_session import PostgresSession
|
|
7
|
-
from adam.commands.postgres.psql_table_completer import PsqlTableNameCompleter
|
|
8
|
-
from adam.config import Config
|
|
3
|
+
from adam.commands.devices.devices import Devices
|
|
9
4
|
from adam.repl_state import ReplState, RequiredState
|
|
10
|
-
from adam.utils import lines_to_tabular, log, log2
|
|
11
5
|
|
|
12
6
|
class PreviewTable(Command):
|
|
13
7
|
COMMAND = 'preview'
|
|
@@ -25,66 +19,21 @@ class PreviewTable(Command):
|
|
|
25
19
|
return PreviewTable.COMMAND
|
|
26
20
|
|
|
27
21
|
def required(self):
|
|
28
|
-
return RequiredState.CLUSTER_OR_POD
|
|
22
|
+
return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L, RequiredState.EXPORT_DB]
|
|
29
23
|
|
|
30
24
|
def run(self, cmd: str, state: ReplState):
|
|
31
25
|
if not(args := self.args(cmd)):
|
|
32
26
|
return super().run(cmd, state)
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return state
|
|
38
|
-
else:
|
|
39
|
-
if not self.validate_state(state):
|
|
40
|
-
return state
|
|
41
|
-
|
|
42
|
-
if not args:
|
|
43
|
-
def show_tables():
|
|
44
|
-
if state.device == ReplState.P:
|
|
45
|
-
pg = PostgresSession(state.namespace, state.pg_path)
|
|
46
|
-
lines = [db["name"] for db in pg.tables() if db["schema"] == PostgresSession.default_schema()]
|
|
47
|
-
log(lines_to_tabular(lines, separator=','))
|
|
48
|
-
else:
|
|
49
|
-
run_cql(state, f'describe tables', show_out=True)
|
|
50
|
-
|
|
51
|
-
if state.in_repl:
|
|
52
|
-
log2('Table is required.')
|
|
53
|
-
log2()
|
|
54
|
-
log2('Tables:')
|
|
55
|
-
show_tables()
|
|
56
|
-
else:
|
|
57
|
-
log2('* Table is missing.')
|
|
58
|
-
show_tables()
|
|
59
|
-
|
|
60
|
-
Command.display_help()
|
|
61
|
-
|
|
62
|
-
return 'command-missing'
|
|
28
|
+
with self.validate(args, state) as (args, state):
|
|
29
|
+
with validate_args(args, state, at_least=1) as table:
|
|
30
|
+
Devices.of(state).preview(table, state)
|
|
63
31
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
rows = Config().get('preview.rows', 10)
|
|
67
|
-
if state.device == ReplState.P:
|
|
68
|
-
PostgresSession(state.namespace, state.pg_path).run_sql(f'select * from {table} limit {rows}')
|
|
69
|
-
else:
|
|
70
|
-
run_cql(state, f'select * from {table} limit {rows}', show_out=True, use_single_quotes=True)
|
|
71
|
-
|
|
72
|
-
return state
|
|
73
|
-
|
|
74
|
-
def completion(self, state: ReplState):
|
|
75
|
-
if state.device == ReplState.P:
|
|
76
|
-
return {PreviewTable.COMMAND: PsqlTableNameCompleter(state.namespace, state.pg_path)}
|
|
77
|
-
elif state.sts:
|
|
78
|
-
return {PreviewTable.COMMAND: CqlTableNameCompleter(table_names(state))}
|
|
32
|
+
return state
|
|
79
33
|
|
|
34
|
+
def completion(self, _: ReplState):
|
|
35
|
+
# taken care of by the sql completer
|
|
80
36
|
return {}
|
|
81
37
|
|
|
82
38
|
def help(self, _: ReplState):
|
|
83
|
-
return f'{PreviewTable.COMMAND} TABLE\t preview table'
|
|
84
|
-
|
|
85
|
-
@functools.lru_cache()
|
|
86
|
-
def cql_tables(state: ReplState):
|
|
87
|
-
if state.pod:
|
|
88
|
-
return tables(state)
|
|
89
|
-
|
|
90
|
-
return tables(state, on_any=True)
|
|
39
|
+
return f'{PreviewTable.COMMAND} TABLE\t preview table'
|
adam/commands/pwd.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from adam.app_session import AppSession
|
|
2
2
|
from adam.commands.command import Command
|
|
3
|
-
from adam.commands.
|
|
3
|
+
from adam.commands.devices.devices import Devices
|
|
4
4
|
from adam.repl_state import ReplState
|
|
5
|
-
from adam.utils import
|
|
5
|
+
from adam.utils import tabulize, log, log_exc
|
|
6
6
|
|
|
7
7
|
class Pwd(Command):
|
|
8
8
|
COMMAND = 'pwd'
|
|
@@ -23,52 +23,20 @@ class Pwd(Command):
|
|
|
23
23
|
if not(args := self.args(cmd)):
|
|
24
24
|
return super().run(cmd, state)
|
|
25
25
|
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
32
|
+
tabulize([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
|
+
], header='DEVICE\tLOCATION', separator='\t')
|
|
37
|
+
log()
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
pg = PostgresSession(state.namespace, state.pg_path)
|
|
33
|
-
|
|
34
|
-
if pg.host:
|
|
35
|
-
words.append(f'host/{pg.host}')
|
|
36
|
-
if pg.db:
|
|
37
|
-
words.append(f'database/{pg.db}')
|
|
38
|
-
elif device == ReplState.A:
|
|
39
|
-
if state.app_env:
|
|
40
|
-
words.append(f'env/{state.app_env}')
|
|
41
|
-
if state.app_app:
|
|
42
|
-
words.append(f'app/{state.app_app}')
|
|
43
|
-
elif device == ReplState.L:
|
|
44
|
-
pass
|
|
45
|
-
else:
|
|
46
|
-
if state.sts:
|
|
47
|
-
words.append(f'sts/{state.sts}')
|
|
48
|
-
if state.pod:
|
|
49
|
-
words.append(f'pod/{state.pod}')
|
|
50
|
-
|
|
51
|
-
return '\t'.join([f'{device}:>'] + (words if words else ['/']))
|
|
52
|
-
|
|
53
|
-
host = "unknown"
|
|
54
|
-
try:
|
|
55
|
-
app_session: AppSession = AppSession.create('c3', 'c3')
|
|
56
|
-
host = app_session.host
|
|
57
|
-
except:
|
|
58
|
-
pass
|
|
59
|
-
|
|
60
|
-
log(lines_to_tabular([
|
|
61
|
-
device_line(state, ReplState.A),
|
|
62
|
-
device_line(state, ReplState.C),
|
|
63
|
-
device_line(state, ReplState.L),
|
|
64
|
-
device_line(state, ReplState.P),
|
|
65
|
-
f'',
|
|
66
|
-
f'HOST\t{host}',
|
|
67
|
-
f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
|
|
68
|
-
], 'DEVICE\tLOCATION', separator='\t'))
|
|
69
|
-
log()
|
|
70
|
-
|
|
71
|
-
return state
|
|
39
|
+
return state
|
|
72
40
|
|
|
73
41
|
def completion(self, state: ReplState):
|
|
74
42
|
return super().completion(state)
|
adam/commands/reaper/reaper.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
|
-
from adam.commands.
|
|
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(
|
|
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,32 +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
|
|
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
|
-
return super().intermediate_run(cmd, state, args, Reaper.cmd_list())
|
|
41
|
-
|
|
42
|
-
def cmd_list():
|
|
28
|
+
def cmd_list(self):
|
|
43
29
|
return [ReaperSchedules(), ReaperScheduleStop(), ReaperScheduleActivate(), ReaperScheduleStart(),
|
|
44
30
|
ReaperForwardStop(), ReaperForward(), ReaperRunAbort(), ReaperRunsAbort(), ReaperRestart(),
|
|
45
31
|
ReaperRuns(), ReaperStatus()]
|
|
46
32
|
|
|
47
|
-
def completion(self, state: ReplState):
|
|
48
|
-
if state.sts:
|
|
49
|
-
return super().completion(state)
|
|
50
|
-
|
|
51
|
-
return {}
|
|
52
|
-
|
|
53
33
|
class ReaperCommandHelper(click.Command):
|
|
54
34
|
def get_help(self, ctx: click.Context):
|
|
55
|
-
|
|
35
|
+
IntermediateCommand.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper().cmd_list(), show_cluster_help=True)
|