kaqing 1.77.0__py3-none-any.whl → 2.0.171__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 +1 -0
- adam/app_session.py +182 -0
- {walker → adam}/apps.py +8 -24
- {walker → adam}/batch.py +54 -97
- {walker → adam}/checks/check.py +3 -3
- {walker → adam}/checks/check_result.py +1 -1
- adam/checks/check_utils.py +65 -0
- {walker → adam}/checks/compactionstats.py +6 -6
- {walker → adam}/checks/cpu.py +14 -8
- adam/checks/cpu_metrics.py +52 -0
- {walker → adam}/checks/disk.py +6 -6
- {walker → adam}/checks/gossip.py +5 -5
- {walker → adam}/checks/memory.py +7 -7
- {walker → adam}/checks/status.py +5 -5
- {walker → adam}/cli.py +3 -3
- {walker → adam}/columns/column.py +1 -1
- adam/columns/columns.py +45 -0
- {walker → adam}/columns/compactions.py +5 -5
- {walker → adam}/columns/cpu.py +6 -4
- adam/columns/cpu_metrics.py +22 -0
- {walker → adam}/columns/dir_data.py +3 -3
- {walker → adam}/columns/dir_snapshots.py +3 -3
- {walker → adam}/columns/gossip.py +5 -5
- {walker → adam}/columns/host_id.py +3 -3
- {walker → adam}/columns/memory.py +3 -3
- {walker → adam}/columns/node_address.py +3 -3
- {walker → adam}/columns/node_load.py +3 -3
- {walker → adam}/columns/node_owns.py +3 -3
- {walker → adam}/columns/node_status.py +3 -3
- {walker → adam}/columns/node_tokens.py +3 -3
- {walker → adam}/columns/node_utils.py +2 -2
- {walker → adam}/columns/pod_name.py +2 -2
- {walker → adam}/columns/volume_cassandra.py +4 -4
- {walker → adam}/columns/volume_root.py +3 -3
- adam/commands/__init__.py +15 -0
- adam/commands/alter_tables.py +81 -0
- adam/commands/app_cmd.py +38 -0
- {walker → adam}/commands/app_ping.py +10 -16
- adam/commands/audit/audit.py +84 -0
- adam/commands/audit/audit_repair_tables.py +74 -0
- adam/commands/audit/audit_run.py +50 -0
- adam/commands/audit/show_last10.py +48 -0
- adam/commands/audit/show_slow10.py +47 -0
- adam/commands/audit/show_top10.py +45 -0
- adam/commands/audit/utils_show_top10.py +59 -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/cat.py +50 -0
- adam/commands/cd.py +43 -0
- adam/commands/check.py +73 -0
- {walker → adam}/commands/cli_commands.py +7 -8
- adam/commands/code.py +57 -0
- adam/commands/command.py +190 -0
- {walker → adam}/commands/command_helpers.py +1 -1
- {walker → adam}/commands/commands_utils.py +15 -25
- adam/commands/cp.py +89 -0
- adam/commands/cql/cql_completions.py +33 -0
- {walker/commands → adam/commands/cql}/cqlsh.py +20 -35
- adam/commands/cql/utils_cql.py +343 -0
- {walker/commands/frontend → adam/commands/deploy}/code_start.py +11 -14
- adam/commands/deploy/code_stop.py +40 -0
- {walker/commands/frontend → adam/commands/deploy}/code_utils.py +7 -9
- adam/commands/deploy/deploy.py +25 -0
- adam/commands/deploy/deploy_frontend.py +49 -0
- adam/commands/deploy/deploy_pg_agent.py +35 -0
- adam/commands/deploy/deploy_pod.py +108 -0
- adam/commands/deploy/deploy_utils.py +29 -0
- adam/commands/deploy/undeploy.py +25 -0
- adam/commands/deploy/undeploy_frontend.py +38 -0
- adam/commands/deploy/undeploy_pg_agent.py +39 -0
- adam/commands/deploy/undeploy_pod.py +48 -0
- adam/commands/devices/device.py +118 -0
- adam/commands/devices/device_app.py +173 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +185 -0
- adam/commands/devices/device_export.py +86 -0
- adam/commands/devices/device_postgres.py +144 -0
- adam/commands/devices/devices.py +25 -0
- {walker → adam}/commands/exit.py +3 -6
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +51 -0
- adam/commands/export/drop_export_database.py +55 -0
- adam/commands/export/drop_export_databases.py +43 -0
- adam/commands/export/export.py +53 -0
- adam/commands/export/export_databases.py +170 -0
- adam/commands/export/export_handlers.py +71 -0
- adam/commands/export/export_select.py +81 -0
- adam/commands/export/export_select_x.py +54 -0
- adam/commands/export/export_use.py +52 -0
- adam/commands/export/exporter.py +352 -0
- adam/commands/export/import_session.py +40 -0
- adam/commands/export/importer.py +67 -0
- adam/commands/export/importer_athena.py +80 -0
- adam/commands/export/importer_sqlite.py +47 -0
- adam/commands/export/show_column_counts.py +54 -0
- adam/commands/export/show_export_databases.py +36 -0
- adam/commands/export/show_export_session.py +48 -0
- adam/commands/export/show_export_sessions.py +44 -0
- adam/commands/export/utils_export.py +314 -0
- {walker → adam}/commands/help.py +17 -12
- adam/commands/intermediate_command.py +49 -0
- adam/commands/issues.py +43 -0
- adam/commands/kubectl.py +38 -0
- adam/commands/login.py +70 -0
- {walker → adam}/commands/logs.py +8 -10
- adam/commands/ls.py +41 -0
- adam/commands/medusa/medusa.py +27 -0
- adam/commands/medusa/medusa_backup.py +57 -0
- adam/commands/medusa/medusa_restore.py +83 -0
- adam/commands/medusa/medusa_show_backupjobs.py +51 -0
- adam/commands/medusa/medusa_show_restorejobs.py +47 -0
- {walker → adam}/commands/nodetool.py +17 -21
- {walker → adam}/commands/param_get.py +15 -16
- adam/commands/param_set.py +43 -0
- adam/commands/postgres/postgres.py +104 -0
- adam/commands/postgres/postgres_context.py +274 -0
- {walker → adam}/commands/postgres/postgres_ls.py +7 -11
- {walker → adam}/commands/postgres/postgres_preview.py +8 -13
- adam/commands/postgres/psql_completions.py +10 -0
- adam/commands/postgres/utils_postgres.py +66 -0
- adam/commands/preview_table.py +37 -0
- adam/commands/pwd.py +47 -0
- adam/commands/reaper/reaper.py +35 -0
- adam/commands/reaper/reaper_forward.py +93 -0
- adam/commands/reaper/reaper_forward_session.py +6 -0
- {walker → adam}/commands/reaper/reaper_forward_stop.py +13 -19
- {walker → adam}/commands/reaper/reaper_restart.py +10 -17
- adam/commands/reaper/reaper_run_abort.py +46 -0
- adam/commands/reaper/reaper_runs.py +82 -0
- adam/commands/reaper/reaper_runs_abort.py +63 -0
- adam/commands/reaper/reaper_schedule_activate.py +45 -0
- adam/commands/reaper/reaper_schedule_start.py +45 -0
- adam/commands/reaper/reaper_schedule_stop.py +45 -0
- {walker → adam}/commands/reaper/reaper_schedules.py +6 -16
- {walker → adam}/commands/reaper/reaper_status.py +11 -19
- adam/commands/reaper/utils_reaper.py +196 -0
- adam/commands/repair/repair.py +26 -0
- {walker → adam}/commands/repair/repair_log.py +7 -10
- adam/commands/repair/repair_run.py +70 -0
- adam/commands/repair/repair_scan.py +71 -0
- {walker → adam}/commands/repair/repair_stop.py +8 -11
- adam/commands/report.py +61 -0
- adam/commands/restart.py +60 -0
- {walker → adam}/commands/rollout.py +25 -30
- adam/commands/shell.py +34 -0
- adam/commands/show/show.py +39 -0
- walker/commands/show/show_version.py → adam/commands/show/show_adam.py +14 -10
- adam/commands/show/show_app_actions.py +57 -0
- {walker → adam}/commands/show/show_app_id.py +12 -15
- {walker → adam}/commands/show/show_app_queues.py +9 -12
- adam/commands/show/show_cassandra_repairs.py +38 -0
- adam/commands/show/show_cassandra_status.py +124 -0
- {walker → adam}/commands/show/show_cassandra_version.py +6 -16
- adam/commands/show/show_commands.py +59 -0
- walker/commands/show/show_storage.py → adam/commands/show/show_host.py +11 -13
- adam/commands/show/show_login.py +62 -0
- {walker → adam}/commands/show/show_params.py +4 -4
- adam/commands/show/show_processes.py +51 -0
- adam/commands/show/show_storage.py +42 -0
- adam/commands/watch.py +82 -0
- {walker → adam}/config.py +10 -22
- {walker → adam}/embedded_apps.py +1 -1
- adam/embedded_params.py +2 -0
- adam/log.py +47 -0
- {walker → adam}/pod_exec_result.py +10 -2
- adam/repl.py +182 -0
- adam/repl_commands.py +124 -0
- adam/repl_state.py +458 -0
- adam/sql/__init__.py +0 -0
- adam/sql/sql_completer.py +120 -0
- adam/sql/sql_state_machine.py +618 -0
- adam/sql/term_completer.py +76 -0
- adam/sso/__init__.py +0 -0
- {walker → adam}/sso/authenticator.py +5 -1
- adam/sso/authn_ad.py +170 -0
- {walker → adam}/sso/authn_okta.py +39 -22
- adam/sso/cred_cache.py +60 -0
- adam/sso/id_token.py +23 -0
- adam/sso/idp.py +143 -0
- adam/sso/idp_login.py +50 -0
- adam/sso/idp_session.py +55 -0
- adam/sso/sso_config.py +63 -0
- adam/utils.py +679 -0
- adam/utils_app.py +98 -0
- adam/utils_athena.py +145 -0
- adam/utils_audits.py +106 -0
- adam/utils_issues.py +32 -0
- adam/utils_k8s/__init__.py +0 -0
- adam/utils_k8s/app_clusters.py +28 -0
- adam/utils_k8s/app_pods.py +33 -0
- adam/utils_k8s/cassandra_clusters.py +36 -0
- adam/utils_k8s/cassandra_nodes.py +33 -0
- adam/utils_k8s/config_maps.py +34 -0
- {walker/k8s_utils → adam/utils_k8s}/custom_resources.py +7 -2
- adam/utils_k8s/deployment.py +56 -0
- {walker/k8s_utils → adam/utils_k8s}/ingresses.py +3 -4
- {walker/k8s_utils → adam/utils_k8s}/jobs.py +3 -3
- adam/utils_k8s/k8s.py +87 -0
- {walker/k8s_utils → adam/utils_k8s}/kube_context.py +4 -4
- adam/utils_k8s/pods.py +290 -0
- {walker/k8s_utils → adam/utils_k8s}/secrets.py +8 -4
- adam/utils_k8s/service_accounts.py +170 -0
- {walker/k8s_utils → adam/utils_k8s}/services.py +3 -4
- {walker/k8s_utils → adam/utils_k8s}/statefulsets.py +6 -16
- {walker/k8s_utils → adam/utils_k8s}/volumes.py +10 -1
- adam/utils_net.py +24 -0
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +46 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +109 -0
- adam/version.py +5 -0
- {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/METADATA +1 -1
- kaqing-2.0.171.dist-info/RECORD +236 -0
- kaqing-2.0.171.dist-info/entry_points.txt +3 -0
- kaqing-2.0.171.dist-info/top_level.txt +1 -0
- kaqing-1.77.0.dist-info/RECORD +0 -159
- kaqing-1.77.0.dist-info/entry_points.txt +0 -3
- kaqing-1.77.0.dist-info/top_level.txt +0 -1
- walker/__init__.py +0 -3
- walker/app_session.py +0 -168
- walker/checks/check_utils.py +0 -97
- walker/columns/columns.py +0 -43
- walker/commands/add_user.py +0 -68
- walker/commands/app.py +0 -67
- walker/commands/bash.py +0 -87
- walker/commands/cd.py +0 -115
- walker/commands/check.py +0 -68
- walker/commands/command.py +0 -104
- walker/commands/cp.py +0 -95
- walker/commands/cql_utils.py +0 -53
- walker/commands/devices.py +0 -89
- walker/commands/frontend/code_stop.py +0 -57
- walker/commands/frontend/setup.py +0 -60
- walker/commands/frontend/setup_frontend.py +0 -58
- walker/commands/frontend/teardown.py +0 -61
- walker/commands/frontend/teardown_frontend.py +0 -42
- walker/commands/issues.py +0 -69
- walker/commands/login.py +0 -72
- walker/commands/ls.py +0 -145
- walker/commands/medusa/medusa.py +0 -69
- walker/commands/medusa/medusa_backup.py +0 -61
- walker/commands/medusa/medusa_restore.py +0 -86
- walker/commands/medusa/medusa_show_backupjobs.py +0 -52
- walker/commands/medusa/medusa_show_restorejobs.py +0 -52
- walker/commands/param_set.py +0 -44
- walker/commands/postgres/postgres.py +0 -113
- walker/commands/postgres/postgres_session.py +0 -225
- walker/commands/preview_table.py +0 -98
- walker/commands/processes.py +0 -53
- walker/commands/pwd.py +0 -64
- walker/commands/reaper/reaper.py +0 -78
- walker/commands/reaper/reaper_forward.py +0 -100
- walker/commands/reaper/reaper_run_abort.py +0 -65
- walker/commands/reaper/reaper_runs.py +0 -97
- walker/commands/reaper/reaper_runs_abort.py +0 -83
- walker/commands/reaper/reaper_schedule_activate.py +0 -64
- walker/commands/reaper/reaper_schedule_start.py +0 -64
- walker/commands/reaper/reaper_schedule_stop.py +0 -64
- walker/commands/reaper/reaper_session.py +0 -159
- walker/commands/repair/repair.py +0 -68
- walker/commands/repair/repair_run.py +0 -72
- walker/commands/repair/repair_scan.py +0 -79
- walker/commands/report.py +0 -57
- walker/commands/restart.py +0 -61
- walker/commands/show/show.py +0 -72
- walker/commands/show/show_app_actions.py +0 -53
- walker/commands/show/show_cassandra_status.py +0 -35
- walker/commands/show/show_commands.py +0 -58
- walker/commands/show/show_processes.py +0 -35
- walker/commands/show/show_repairs.py +0 -47
- walker/commands/status.py +0 -128
- walker/commands/storage.py +0 -52
- walker/commands/user_entry.py +0 -69
- walker/commands/watch.py +0 -85
- walker/embedded_params.py +0 -2
- walker/k8s_utils/cassandra_clusters.py +0 -48
- walker/k8s_utils/cassandra_nodes.py +0 -26
- walker/k8s_utils/pods.py +0 -211
- walker/repl.py +0 -165
- walker/repl_commands.py +0 -58
- walker/repl_state.py +0 -211
- walker/sso/authn_ad.py +0 -94
- walker/sso/idp.py +0 -150
- walker/sso/idp_login.py +0 -29
- walker/sso/sso_config.py +0 -45
- walker/utils.py +0 -194
- walker/version.py +0 -5
- {walker → adam}/checks/__init__.py +0 -0
- {walker → adam}/checks/check_context.py +0 -0
- {walker → adam}/checks/issue.py +0 -0
- {walker → adam}/cli_group.py +0 -0
- {walker → adam}/columns/__init__.py +0 -0
- {walker/commands → adam/commands/audit}/__init__.py +0 -0
- {walker/commands/frontend → adam/commands/cql}/__init__.py +0 -0
- {walker/commands/medusa → adam/commands/deploy}/__init__.py +0 -0
- {walker/commands/postgres → adam/commands/devices}/__init__.py +0 -0
- {walker/commands/reaper → adam/commands/export}/__init__.py +0 -0
- {walker/commands/repair → adam/commands/medusa}/__init__.py +0 -0
- {walker → adam}/commands/nodetool_commands.py +0 -0
- {walker/commands/show → adam/commands/postgres}/__init__.py +0 -0
- {walker/k8s_utils → adam/commands/reaper}/__init__.py +0 -0
- {walker/sso → adam/commands/repair}/__init__.py +0 -0
- /walker/medusa_show_restorejobs.py → /adam/commands/show/__init__.py +0 -0
- {walker → adam}/repl_session.py +0 -0
- {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import functools
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
from adam.config import Config
|
|
7
|
+
from adam.repl_session import ReplSession
|
|
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
|
|
12
|
+
|
|
13
|
+
class PostgresContext:
|
|
14
|
+
def apply(namespace: str, path: str, arg: str = None) -> 'PostgresContext':
|
|
15
|
+
context = PostgresContext(namespace, path)
|
|
16
|
+
|
|
17
|
+
if arg:
|
|
18
|
+
if arg == '..':
|
|
19
|
+
if context.db:
|
|
20
|
+
context.db = None
|
|
21
|
+
else:
|
|
22
|
+
context.host = None
|
|
23
|
+
else:
|
|
24
|
+
tks = arg.split('@')
|
|
25
|
+
if not context.host:
|
|
26
|
+
context.host = tks[0]
|
|
27
|
+
else:
|
|
28
|
+
context.db = tks[0]
|
|
29
|
+
|
|
30
|
+
if not namespace and tks[1]:
|
|
31
|
+
context.namespace = tks[1]
|
|
32
|
+
|
|
33
|
+
return context
|
|
34
|
+
|
|
35
|
+
def __init__(self, ns: str, path: str):
|
|
36
|
+
self.namespace = ns
|
|
37
|
+
self.conn_details = None
|
|
38
|
+
self.host = None
|
|
39
|
+
self.db = None
|
|
40
|
+
|
|
41
|
+
if path:
|
|
42
|
+
tks = path.split('/')
|
|
43
|
+
hn = tks[0].split('@')
|
|
44
|
+
self.host = hn[0]
|
|
45
|
+
if len(hn) > 1 and not ns:
|
|
46
|
+
self.namespace = hn[1]
|
|
47
|
+
|
|
48
|
+
if len(tks) > 1:
|
|
49
|
+
self.db = tks[1]
|
|
50
|
+
|
|
51
|
+
def path(self):
|
|
52
|
+
if not self.host:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
d = self.host
|
|
56
|
+
if not self.db:
|
|
57
|
+
return d
|
|
58
|
+
|
|
59
|
+
return f'{self.host}/{self.db}'
|
|
60
|
+
|
|
61
|
+
def hosts(ns: str):
|
|
62
|
+
return PostgresContext.hosts_for_namespace(ns)
|
|
63
|
+
|
|
64
|
+
@functools.lru_cache()
|
|
65
|
+
def hosts_for_namespace(ns: str):
|
|
66
|
+
ss = Secrets.list_secrets(ns, name_pattern=Config().get('pg.name-pattern', '^{namespace}.*k8spg.*'))
|
|
67
|
+
|
|
68
|
+
def excludes(name: str):
|
|
69
|
+
exs = Config().get('pg.excludes', '.helm., -admin-secret')
|
|
70
|
+
if exs:
|
|
71
|
+
for ex in exs.split(','):
|
|
72
|
+
if ex.strip(' ') in name:
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
return [s for s in ss if not excludes(s)]
|
|
78
|
+
|
|
79
|
+
def databases(self):
|
|
80
|
+
dbs = []
|
|
81
|
+
# List of databases
|
|
82
|
+
# Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
|
|
83
|
+
# ---------------------------------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
|
|
84
|
+
# postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
|
|
85
|
+
# stgawsscpsr_c3_c3 | postgres | UTF8 | C | C | | libc |
|
|
86
|
+
# template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
|
|
87
|
+
# | | | | | | | postgres=CTc/postgres
|
|
88
|
+
# (48 rows)
|
|
89
|
+
if r := self.run_sql('\l', show_out=False):
|
|
90
|
+
s = 0
|
|
91
|
+
for line in r.stdout.split('\n'):
|
|
92
|
+
line: str = line.strip(' \r')
|
|
93
|
+
if s == 0:
|
|
94
|
+
if 'List of databases' in line:
|
|
95
|
+
s = 1
|
|
96
|
+
elif s == 1:
|
|
97
|
+
if 'Name' in line and 'Owner' in line and 'Encoding' in line:
|
|
98
|
+
s = 2
|
|
99
|
+
elif s == 2:
|
|
100
|
+
if line.startswith('---------'):
|
|
101
|
+
s = 3
|
|
102
|
+
elif s == 3:
|
|
103
|
+
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
104
|
+
if groups and groups[1] != '|':
|
|
105
|
+
dbs.append({'name': groups[1], 'owner': groups[2]})
|
|
106
|
+
|
|
107
|
+
return dbs
|
|
108
|
+
|
|
109
|
+
def tables(self):
|
|
110
|
+
dbs = []
|
|
111
|
+
# List of relations
|
|
112
|
+
# Schema | Name | Type | Owner
|
|
113
|
+
# ----------+------------------------------------------------------------+-------+---------------
|
|
114
|
+
# postgres | c3_2_admin_aclpriv | table | postgres
|
|
115
|
+
# postgres | c3_2_admin_aclpriv_a | table | postgres
|
|
116
|
+
if r := self.run_sql('\dt', show_out=False):
|
|
117
|
+
s = 0
|
|
118
|
+
for line in r.stdout.split('\n'):
|
|
119
|
+
line: str = line.strip(' \r')
|
|
120
|
+
if s == 0:
|
|
121
|
+
if 'List of relations' in line:
|
|
122
|
+
s = 1
|
|
123
|
+
elif s == 1:
|
|
124
|
+
if 'Schema' in line and 'Name' in line and 'Type' in line:
|
|
125
|
+
s = 2
|
|
126
|
+
elif s == 2:
|
|
127
|
+
if line.startswith('---------'):
|
|
128
|
+
s = 3
|
|
129
|
+
elif s == 3:
|
|
130
|
+
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
131
|
+
if groups and groups[1] != '|':
|
|
132
|
+
dbs.append({'schema': groups[1], 'name': groups[2]})
|
|
133
|
+
|
|
134
|
+
return dbs
|
|
135
|
+
|
|
136
|
+
def run_sql(self, sql: str, show_out = True, background = False):
|
|
137
|
+
db = self.db if self.db else PostgresContext.default_db()
|
|
138
|
+
|
|
139
|
+
if KubeContext.in_cluster():
|
|
140
|
+
cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
|
|
141
|
+
log2(f'{cmd1} "{sql}"')
|
|
142
|
+
# remove double quotes from the sql argument
|
|
143
|
+
cmd = cmd1.split(' ') + [sql]
|
|
144
|
+
|
|
145
|
+
r = subprocess.run(cmd, capture_output=not background, text=True)
|
|
146
|
+
if show_out:
|
|
147
|
+
log2(r.stdout)
|
|
148
|
+
log2(r.stderr)
|
|
149
|
+
|
|
150
|
+
return r
|
|
151
|
+
else:
|
|
152
|
+
pod_name, container_name = PostgresContext.pod_and_container(self.namespace)
|
|
153
|
+
if not pod_name:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
# ns = self.namespace
|
|
157
|
+
# pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
|
|
158
|
+
|
|
159
|
+
# if Config().get('pg.agent.just-in-time', False):
|
|
160
|
+
# if not PostgresContext.deploy_pg_agent(pod_name, ns):
|
|
161
|
+
# return
|
|
162
|
+
|
|
163
|
+
# real_pod_name = pod_name
|
|
164
|
+
# try:
|
|
165
|
+
# # try with dedicated pg agent pod name configured
|
|
166
|
+
# Pods.get(ns, pod_name)
|
|
167
|
+
# except:
|
|
168
|
+
# try:
|
|
169
|
+
# # try with the ops pod
|
|
170
|
+
# pod_name = Config().get('pod.name', 'ops')
|
|
171
|
+
# real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
|
|
172
|
+
# except:
|
|
173
|
+
# log2(f"Could not locate {pod_name} pod.")
|
|
174
|
+
# return None
|
|
175
|
+
|
|
176
|
+
cmd = f'psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
|
|
177
|
+
env_prefix = f'PGPASSWORD="{self.password()}"'
|
|
178
|
+
|
|
179
|
+
r = Pods.exec(pod_name, container_name, self.namespace, cmd, show_out=show_out, background=background, env_prefix=env_prefix)
|
|
180
|
+
if r and Config().get('repl.history.push-cat-remote-log-file', True):
|
|
181
|
+
if r.log_file and ReplSession().prompt_session:
|
|
182
|
+
ReplSession().prompt_session.history.append_string(f'@{r.pod} cat {r.log_file}')
|
|
183
|
+
|
|
184
|
+
return r
|
|
185
|
+
|
|
186
|
+
def pod_and_container(ns: str):
|
|
187
|
+
container_name = Config().get('pg.agent.name', 'ops-pg-agent')
|
|
188
|
+
|
|
189
|
+
if Config().get('pg.agent.just-in-time', False):
|
|
190
|
+
if not PostgresContext.deploy_pg_agent(container_name, ns):
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
pod_name = container_name
|
|
194
|
+
try:
|
|
195
|
+
# try with dedicated pg agent pod name configured
|
|
196
|
+
Pods.get(ns, container_name)
|
|
197
|
+
except:
|
|
198
|
+
try:
|
|
199
|
+
# try with the ops pod
|
|
200
|
+
container_name = Config().get('pod.name', 'ops')
|
|
201
|
+
pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
|
|
202
|
+
except:
|
|
203
|
+
log2(f"Could not locate {container_name} pod.")
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
return pod_name, container_name
|
|
207
|
+
|
|
208
|
+
def deploy_pg_agent(pod_name: str, ns: str) -> str:
|
|
209
|
+
image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
|
|
210
|
+
timeout = Config().get('pg.agent.timeout', 3600)
|
|
211
|
+
try:
|
|
212
|
+
Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
|
|
213
|
+
except Exception as e:
|
|
214
|
+
if e.status == 409:
|
|
215
|
+
if Pods.completed(ns, pod_name):
|
|
216
|
+
try:
|
|
217
|
+
Pods.delete(pod_name, ns)
|
|
218
|
+
Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
|
|
219
|
+
except Exception as e2:
|
|
220
|
+
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
|
|
221
|
+
|
|
222
|
+
return
|
|
223
|
+
else:
|
|
224
|
+
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
|
|
225
|
+
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
Pods.wait_for_running(ns, pod_name)
|
|
229
|
+
|
|
230
|
+
return pod_name
|
|
231
|
+
|
|
232
|
+
def undeploy_pg_agent(pod_name: str, ns: str):
|
|
233
|
+
Pods.delete(pod_name, ns, grace_period_seconds=0)
|
|
234
|
+
|
|
235
|
+
def endpoint(self):
|
|
236
|
+
if not self.conn_details:
|
|
237
|
+
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
238
|
+
|
|
239
|
+
endpoint_key = Config().get('pg.secret.endpoint-key', 'postgres-db-endpoint')
|
|
240
|
+
|
|
241
|
+
return self.conn_details[endpoint_key] if endpoint_key in self.conn_details else ''
|
|
242
|
+
|
|
243
|
+
def port(self):
|
|
244
|
+
if not self.conn_details:
|
|
245
|
+
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
246
|
+
|
|
247
|
+
port_key = Config().get('pg.secret.port-key', 'postgres-db-port')
|
|
248
|
+
|
|
249
|
+
return self.conn_details[port_key] if port_key in self.conn_details else ''
|
|
250
|
+
|
|
251
|
+
def username(self):
|
|
252
|
+
if not self.conn_details:
|
|
253
|
+
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
254
|
+
|
|
255
|
+
username_key = Config().get('pg.secret.username-key', 'postgres-admin-username')
|
|
256
|
+
|
|
257
|
+
return self.conn_details[username_key] if username_key in self.conn_details else ''
|
|
258
|
+
|
|
259
|
+
def password(self):
|
|
260
|
+
if not self.conn_details:
|
|
261
|
+
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
262
|
+
|
|
263
|
+
password_key = Config().get('pg.secret.password-key', 'postgres-admin-password')
|
|
264
|
+
|
|
265
|
+
return self.conn_details[password_key] if password_key in self.conn_details else ''
|
|
266
|
+
|
|
267
|
+
def default_db():
|
|
268
|
+
return Config().get('pg.default-db', 'postgres')
|
|
269
|
+
|
|
270
|
+
def default_owner():
|
|
271
|
+
return Config().get('pg.default-owner', 'postgres')
|
|
272
|
+
|
|
273
|
+
def default_schema():
|
|
274
|
+
return Config().get('pg.default-schema', 'postgres')
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
from
|
|
1
|
+
from adam.commands.command import Command
|
|
2
|
+
from adam.commands.ls import Ls
|
|
3
|
+
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:
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
from
|
|
4
|
-
from walker.repl_state import ReplState, RequiredState
|
|
1
|
+
from adam.commands.command import Command
|
|
2
|
+
from adam.commands.preview_table import PreviewTable
|
|
3
|
+
from adam.repl_state import ReplState, RequiredState
|
|
5
4
|
|
|
6
5
|
class PostgresPreview(Command):
|
|
7
6
|
COMMAND = 'pg preview'
|
|
8
|
-
reaper_login = None
|
|
9
7
|
|
|
10
8
|
# the singleton pattern
|
|
11
9
|
def __new__(cls, *args, **kwargs):
|
|
@@ -20,21 +18,18 @@ class PostgresPreview(Command):
|
|
|
20
18
|
return PostgresPreview.COMMAND
|
|
21
19
|
|
|
22
20
|
def required(self):
|
|
23
|
-
return RequiredState.
|
|
21
|
+
return RequiredState.PG_DATABASE
|
|
24
22
|
|
|
25
23
|
def run(self, cmd: str, state: ReplState):
|
|
26
24
|
if not(args := self.args(cmd)):
|
|
27
25
|
return super().run(cmd, state)
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return state
|
|
32
|
-
|
|
33
|
-
state.device = ReplState.P
|
|
27
|
+
with self.validate(args, state) as (args, state):
|
|
28
|
+
state.device = ReplState.P
|
|
34
29
|
|
|
35
|
-
|
|
30
|
+
PreviewTable().run(f'preview {" ".join(args)}', state)
|
|
36
31
|
|
|
37
|
-
|
|
32
|
+
return state
|
|
38
33
|
|
|
39
34
|
def completion(self, state: ReplState):
|
|
40
35
|
if state.sts:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from adam.commands.postgres.utils_postgres import pg_table_names
|
|
2
|
+
from adam.sql.sql_completer import SqlCompleter
|
|
3
|
+
|
|
4
|
+
def psql_completions(ns: str, pg_path: str):
|
|
5
|
+
return {
|
|
6
|
+
'\h': None,
|
|
7
|
+
'\d': None,
|
|
8
|
+
'\dt': None,
|
|
9
|
+
'\du': None
|
|
10
|
+
} | SqlCompleter(lambda: pg_table_names(ns, pg_path)).completions_for_nesting()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
from adam.commands.postgres.postgres_context import PostgresContext
|
|
4
|
+
from adam.repl_state import ReplState
|
|
5
|
+
from adam.utils import log2, wait_log
|
|
6
|
+
from adam.utils_k8s.pods import Pods
|
|
7
|
+
|
|
8
|
+
TestPG = [False]
|
|
9
|
+
|
|
10
|
+
@functools.lru_cache()
|
|
11
|
+
def pg_database_names(ns: str, pg_path: str):
|
|
12
|
+
if TestPG[0]:
|
|
13
|
+
return ['azops88_c3ai_c3']
|
|
14
|
+
|
|
15
|
+
wait_log('Inspecting Postgres Databases...')
|
|
16
|
+
|
|
17
|
+
pg = PostgresContext.apply(ns, pg_path)
|
|
18
|
+
return [db['name'] for db in pg.databases() if db['owner'] == PostgresContext.default_owner()]
|
|
19
|
+
|
|
20
|
+
@functools.lru_cache()
|
|
21
|
+
def pg_table_names(ns: str, pg_path: str):
|
|
22
|
+
if TestPG[0]:
|
|
23
|
+
return ['C3_2_XYZ1']
|
|
24
|
+
|
|
25
|
+
wait_log('Inspecting Postgres Database...')
|
|
26
|
+
return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresContext.default_schema()]
|
|
27
|
+
|
|
28
|
+
def pg_tables(ns: str, pg_path: str):
|
|
29
|
+
pg = PostgresContext.apply(ns, pg_path)
|
|
30
|
+
if pg.db:
|
|
31
|
+
return pg.tables()
|
|
32
|
+
|
|
33
|
+
return []
|
|
34
|
+
|
|
35
|
+
class PostgresPodService:
|
|
36
|
+
def __init__(self, handler: 'PostgresExecHandler'):
|
|
37
|
+
self.handler = handler
|
|
38
|
+
|
|
39
|
+
def exec(self, command: str, show_out=True):
|
|
40
|
+
state = self.handler.state
|
|
41
|
+
|
|
42
|
+
pod, container = PostgresContext.pod_and_container(state.namespace)
|
|
43
|
+
if not pod:
|
|
44
|
+
log2('Cannot locate postgres agent or ops pod.')
|
|
45
|
+
return state
|
|
46
|
+
|
|
47
|
+
return Pods.exec(pod, container, state.namespace, command, show_out=show_out)
|
|
48
|
+
|
|
49
|
+
def sql(self, args: list[str], background=False):
|
|
50
|
+
state = self.handler.state
|
|
51
|
+
|
|
52
|
+
PostgresContext.apply(state.namespace, state.pg_path).run_sql(' '.join(args), background=background)
|
|
53
|
+
|
|
54
|
+
class PostgresExecHandler:
|
|
55
|
+
def __init__(self, state: ReplState, background=False):
|
|
56
|
+
self.state = state
|
|
57
|
+
self.background = background
|
|
58
|
+
|
|
59
|
+
def __enter__(self):
|
|
60
|
+
return PostgresPodService(self)
|
|
61
|
+
|
|
62
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
def postgres(state: ReplState, background=False):
|
|
66
|
+
return PostgresExecHandler(state, background=background)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from adam.commands.command import Command
|
|
2
|
+
from adam.commands.devices.devices import Devices
|
|
3
|
+
from adam.repl_state import ReplState, RequiredState
|
|
4
|
+
|
|
5
|
+
class PreviewTable(Command):
|
|
6
|
+
COMMAND = 'preview'
|
|
7
|
+
|
|
8
|
+
# the singleton pattern
|
|
9
|
+
def __new__(cls, *args, **kwargs):
|
|
10
|
+
if not hasattr(cls, 'instance'): cls.instance = super(PreviewTable, cls).__new__(cls)
|
|
11
|
+
|
|
12
|
+
return cls.instance
|
|
13
|
+
|
|
14
|
+
def __init__(self, successor: Command=None):
|
|
15
|
+
super().__init__(successor)
|
|
16
|
+
|
|
17
|
+
def command(self):
|
|
18
|
+
return PreviewTable.COMMAND
|
|
19
|
+
|
|
20
|
+
def required(self):
|
|
21
|
+
return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L]
|
|
22
|
+
|
|
23
|
+
def run(self, cmd: str, state: ReplState):
|
|
24
|
+
if not(args := self.args(cmd)):
|
|
25
|
+
return super().run(cmd, state)
|
|
26
|
+
|
|
27
|
+
with self.validate(args, state) as (args, state):
|
|
28
|
+
Devices.device(state).preview(args[0] if args else None, state)
|
|
29
|
+
|
|
30
|
+
return state
|
|
31
|
+
|
|
32
|
+
def completion(self, _: ReplState):
|
|
33
|
+
# taken care of by the sql completer
|
|
34
|
+
return {}
|
|
35
|
+
|
|
36
|
+
def help(self, _: ReplState):
|
|
37
|
+
return f'{PreviewTable.COMMAND} TABLE\t preview table'
|
adam/commands/pwd.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from adam.app_session import AppSession
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.commands.devices.devices import Devices
|
|
4
|
+
from adam.repl_state import ReplState
|
|
5
|
+
from adam.utils import lines_to_tabular, log
|
|
6
|
+
|
|
7
|
+
class Pwd(Command):
|
|
8
|
+
COMMAND = 'pwd'
|
|
9
|
+
|
|
10
|
+
# the singleton pattern
|
|
11
|
+
def __new__(cls, *args, **kwargs):
|
|
12
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Pwd, cls).__new__(cls)
|
|
13
|
+
|
|
14
|
+
return cls.instance
|
|
15
|
+
|
|
16
|
+
def __init__(self, successor: Command=None):
|
|
17
|
+
super().__init__(successor)
|
|
18
|
+
|
|
19
|
+
def command(self):
|
|
20
|
+
return Pwd.COMMAND
|
|
21
|
+
|
|
22
|
+
def run(self, cmd: str, state: ReplState):
|
|
23
|
+
if not(args := self.args(cmd)):
|
|
24
|
+
return super().run(cmd, state)
|
|
25
|
+
|
|
26
|
+
with self.validate(args, state) as (_, state):
|
|
27
|
+
host = "unknown"
|
|
28
|
+
try:
|
|
29
|
+
app_session: AppSession = AppSession.create('c3', 'c3')
|
|
30
|
+
host = app_session.host
|
|
31
|
+
except:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
log(lines_to_tabular([device.pwd(state) for device in Devices.all()] + [
|
|
35
|
+
f'',
|
|
36
|
+
f'HOST\t{host}',
|
|
37
|
+
f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
|
|
38
|
+
], 'DEVICE\tLOCATION', separator='\t'))
|
|
39
|
+
log()
|
|
40
|
+
|
|
41
|
+
return state
|
|
42
|
+
|
|
43
|
+
def completion(self, state: ReplState):
|
|
44
|
+
return super().completion(state)
|
|
45
|
+
|
|
46
|
+
def help(self, _: ReplState):
|
|
47
|
+
return f'{Pwd.COMMAND}\t print current working directories'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from adam.commands.intermediate_command import IntermediateCommand
|
|
4
|
+
from .reaper_forward import ReaperForward
|
|
5
|
+
from .reaper_forward_stop import ReaperForwardStop
|
|
6
|
+
from .reaper_restart import ReaperRestart
|
|
7
|
+
from .reaper_run_abort import ReaperRunAbort
|
|
8
|
+
from .reaper_runs import ReaperRuns
|
|
9
|
+
from .reaper_runs_abort import ReaperRunsAbort
|
|
10
|
+
from .reaper_schedule_activate import ReaperScheduleActivate
|
|
11
|
+
from .reaper_schedule_start import ReaperScheduleStart
|
|
12
|
+
from .reaper_schedule_stop import ReaperScheduleStop
|
|
13
|
+
from .reaper_schedules import ReaperSchedules
|
|
14
|
+
from .reaper_status import ReaperStatus
|
|
15
|
+
|
|
16
|
+
class Reaper(IntermediateCommand):
|
|
17
|
+
COMMAND = 'reaper'
|
|
18
|
+
|
|
19
|
+
# the singleton pattern
|
|
20
|
+
def __new__(cls, *args, **kwargs):
|
|
21
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Reaper, cls).__new__(cls)
|
|
22
|
+
|
|
23
|
+
return cls.instance
|
|
24
|
+
|
|
25
|
+
def command(self):
|
|
26
|
+
return Reaper.COMMAND
|
|
27
|
+
|
|
28
|
+
def cmd_list(self):
|
|
29
|
+
return [ReaperSchedules(), ReaperScheduleStop(), ReaperScheduleActivate(), ReaperScheduleStart(),
|
|
30
|
+
ReaperForwardStop(), ReaperForward(), ReaperRunAbort(), ReaperRunsAbort(), ReaperRestart(),
|
|
31
|
+
ReaperRuns(), ReaperStatus()]
|
|
32
|
+
|
|
33
|
+
class ReaperCommandHelper(click.Command):
|
|
34
|
+
def get_help(self, ctx: click.Context):
|
|
35
|
+
IntermediateCommand.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper().cmd_list(), show_cluster_help=True)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from adam.commands.command import Command
|
|
6
|
+
from adam.commands.reaper.reaper_forward_session import ReaperForwardSession
|
|
7
|
+
from adam.commands.reaper.utils_reaper import Reapers, port_forwarding
|
|
8
|
+
from adam.config import Config
|
|
9
|
+
from adam.repl_session import ReplSession
|
|
10
|
+
from adam.repl_state import ReplState, RequiredState
|
|
11
|
+
from adam.utils import lines_to_tabular, log2
|
|
12
|
+
|
|
13
|
+
class ReaperForward(Command):
|
|
14
|
+
COMMAND = 'reaper forward'
|
|
15
|
+
|
|
16
|
+
# the singleton pattern
|
|
17
|
+
def __new__(cls, *args, **kwargs):
|
|
18
|
+
if not hasattr(cls, 'instance'): cls.instance = super(ReaperForward, cls).__new__(cls)
|
|
19
|
+
|
|
20
|
+
return cls.instance
|
|
21
|
+
|
|
22
|
+
def __init__(self, successor: Command=None):
|
|
23
|
+
super().__init__(successor)
|
|
24
|
+
|
|
25
|
+
def command(self):
|
|
26
|
+
return ReaperForward.COMMAND
|
|
27
|
+
|
|
28
|
+
def required(self):
|
|
29
|
+
return RequiredState.CLUSTER
|
|
30
|
+
|
|
31
|
+
def run(self, cmd: str, state: ReplState):
|
|
32
|
+
if not(args := self.args(cmd)):
|
|
33
|
+
return super().run(cmd, state)
|
|
34
|
+
|
|
35
|
+
with self.validate(args, state) as (args, state):
|
|
36
|
+
if not Reapers.pod_name(state):
|
|
37
|
+
return state
|
|
38
|
+
|
|
39
|
+
spec = Reapers.reaper_spec(state)
|
|
40
|
+
if state.in_repl:
|
|
41
|
+
if ReaperForwardSession.is_forwarding:
|
|
42
|
+
log2("Another port-forward is already running.")
|
|
43
|
+
|
|
44
|
+
return "already-running"
|
|
45
|
+
|
|
46
|
+
# make it a daemon to exit with a Ctrl-D
|
|
47
|
+
thread = threading.Thread(target=self.loop, args=(state,), daemon=True)
|
|
48
|
+
thread.start()
|
|
49
|
+
|
|
50
|
+
while not ReaperForwardSession.is_forwarding:
|
|
51
|
+
time.sleep(1)
|
|
52
|
+
|
|
53
|
+
d = {
|
|
54
|
+
'reaper-ui': spec["web-uri"],
|
|
55
|
+
'reaper-username': spec["username"],
|
|
56
|
+
'reaper-password': spec["password"]
|
|
57
|
+
}
|
|
58
|
+
log2()
|
|
59
|
+
log2(lines_to_tabular([f'{k},{v}' for k, v in d.items()], separator=','))
|
|
60
|
+
|
|
61
|
+
for k, v in d.items():
|
|
62
|
+
ReplSession().prompt_session.history.append_string(f'cp {k}')
|
|
63
|
+
log2()
|
|
64
|
+
log2(f'Use <Up> arrow key to copy the values to clipboard.')
|
|
65
|
+
else:
|
|
66
|
+
try:
|
|
67
|
+
log2(f'Click: {spec["web-uri"]}')
|
|
68
|
+
log2(f'username: {spec["username"]}')
|
|
69
|
+
log2(f'password: {spec["password"]}')
|
|
70
|
+
log2()
|
|
71
|
+
log2(f"Press Ctrl+C to break.")
|
|
72
|
+
|
|
73
|
+
time.sleep(Config().get('reaper.port-forward.timeout', 3600 * 24))
|
|
74
|
+
except KeyboardInterrupt:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
return state
|
|
78
|
+
|
|
79
|
+
def loop(self, state: ReplState):
|
|
80
|
+
with port_forwarding(state, Reapers.local_port(), partial(Reapers.svc_or_pod, state), Reapers.target_port()):
|
|
81
|
+
ReaperForwardSession.is_forwarding = True
|
|
82
|
+
try:
|
|
83
|
+
while not ReaperForwardSession.stopping.is_set():
|
|
84
|
+
time.sleep(1)
|
|
85
|
+
finally:
|
|
86
|
+
ReaperForwardSession.stopping.clear()
|
|
87
|
+
ReaperForwardSession.is_forwarding = False
|
|
88
|
+
|
|
89
|
+
def completion(self, state: ReplState):
|
|
90
|
+
return super().completion(state)
|
|
91
|
+
|
|
92
|
+
def help(self, _: ReplState):
|
|
93
|
+
return f'{ReaperForward.COMMAND}\t port-forward to reaper'
|