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
walker/repl_state.py
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import copy
|
|
2
|
-
from enum import Enum
|
|
3
|
-
|
|
4
|
-
from walker.commands.postgres.postgres_session import PostgresSession
|
|
5
|
-
from walker.k8s_utils.cassandra_clusters import CassandraClusters
|
|
6
|
-
from walker.k8s_utils.cassandra_nodes import CassandraNodes
|
|
7
|
-
from walker.k8s_utils.kube_context import KubeContext
|
|
8
|
-
from walker.k8s_utils.secrets import Secrets
|
|
9
|
-
from walker.utils import display_help, log2, random_alphanumeric
|
|
10
|
-
|
|
11
|
-
class BashSession:
|
|
12
|
-
def __init__(self, device: str = None):
|
|
13
|
-
self.session_id = random_alphanumeric(6)
|
|
14
|
-
self.device = device
|
|
15
|
-
|
|
16
|
-
def pwd(self, state: 'ReplState'):
|
|
17
|
-
command = f'cat /tmp/.qing-{self.session_id}'
|
|
18
|
-
|
|
19
|
-
if state.pod:
|
|
20
|
-
rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=False)]
|
|
21
|
-
elif state.sts:
|
|
22
|
-
rs = CassandraClusters.exec(state.sts, state.namespace, command, action='bash', show_out=False)
|
|
23
|
-
|
|
24
|
-
dir = None
|
|
25
|
-
for r in rs:
|
|
26
|
-
if r.exit_code(): # if fails to read the session file, ignore
|
|
27
|
-
continue
|
|
28
|
-
|
|
29
|
-
dir0 = r.stdout.strip(' \r\n')
|
|
30
|
-
if dir:
|
|
31
|
-
if dir != dir0:
|
|
32
|
-
log2('Inconsitent working dir found across multiple pods.')
|
|
33
|
-
return None
|
|
34
|
-
else:
|
|
35
|
-
dir = dir0
|
|
36
|
-
|
|
37
|
-
return dir
|
|
38
|
-
|
|
39
|
-
class RequiredState(Enum):
|
|
40
|
-
CLUSTER = 'cluster'
|
|
41
|
-
POD = 'pod'
|
|
42
|
-
CLUSTER_OR_POD = 'cluster_or_pod'
|
|
43
|
-
NAMESPACE = 'namespace'
|
|
44
|
-
PG_DATABASE = 'pg_database'
|
|
45
|
-
APP_APP = 'app_app'
|
|
46
|
-
|
|
47
|
-
class ReplState:
|
|
48
|
-
A = 'a'
|
|
49
|
-
C = 'c'
|
|
50
|
-
P = 'p'
|
|
51
|
-
|
|
52
|
-
def __init__(self, device: str = None,
|
|
53
|
-
sts: str = None, pod: str = None, namespace: str = None, ns_sts: str = None,
|
|
54
|
-
pg_path: str = None,
|
|
55
|
-
app_env: str = None, app_app: str = None,
|
|
56
|
-
in_repl = False, bash_session: BashSession = None, remote_dir = None):
|
|
57
|
-
self.namespace = KubeContext.in_cluster_namespace()
|
|
58
|
-
|
|
59
|
-
self.device = device
|
|
60
|
-
self.sts = sts
|
|
61
|
-
self.pod = pod
|
|
62
|
-
self.pg_path = pg_path
|
|
63
|
-
self.app_env = app_env
|
|
64
|
-
self.app_app = app_app
|
|
65
|
-
if namespace:
|
|
66
|
-
self.namespace = namespace
|
|
67
|
-
self.in_repl = in_repl
|
|
68
|
-
self.bash_session = bash_session
|
|
69
|
-
self.remote_dir = remote_dir
|
|
70
|
-
self.wait_log_flag = False
|
|
71
|
-
|
|
72
|
-
if ns_sts:
|
|
73
|
-
nn = ns_sts.split('@')
|
|
74
|
-
self.sts = nn[0]
|
|
75
|
-
if len(nn) > 1:
|
|
76
|
-
self.namespace = nn[1]
|
|
77
|
-
|
|
78
|
-
# work for CliCommand.Values()
|
|
79
|
-
def __eq__(self, other: 'ReplState'):
|
|
80
|
-
return self.sts == other.sts and self.pod == other.pod
|
|
81
|
-
|
|
82
|
-
def __hash__(self):
|
|
83
|
-
return hash((self.sts, self.pod))
|
|
84
|
-
|
|
85
|
-
def apply_args(self, args: list[str], cmd: list[str] = None, resolve_pg = True) -> tuple['ReplState', list[str]]:
|
|
86
|
-
state = self
|
|
87
|
-
|
|
88
|
-
new_args = []
|
|
89
|
-
for index, arg in enumerate(args):
|
|
90
|
-
if index < 6:
|
|
91
|
-
state = copy.copy(state)
|
|
92
|
-
|
|
93
|
-
s, n = KubeContext.is_sts_name(arg)
|
|
94
|
-
if s:
|
|
95
|
-
if not state.sts:
|
|
96
|
-
state.sts = s
|
|
97
|
-
if n and not state.namespace:
|
|
98
|
-
state.namespace = n
|
|
99
|
-
|
|
100
|
-
p, n = KubeContext.is_pod_name(arg)
|
|
101
|
-
if p:
|
|
102
|
-
if not state.pod:
|
|
103
|
-
state.pod = p
|
|
104
|
-
if n and not state.namespace:
|
|
105
|
-
state.namespace = n
|
|
106
|
-
|
|
107
|
-
pg = None
|
|
108
|
-
if resolve_pg:
|
|
109
|
-
pg = KubeContext.is_pg_name(arg)
|
|
110
|
-
if pg and not state.pg_path:
|
|
111
|
-
state.pg_path = pg
|
|
112
|
-
|
|
113
|
-
if not s and not p and not pg:
|
|
114
|
-
new_args.append(arg)
|
|
115
|
-
else:
|
|
116
|
-
new_args.append(arg)
|
|
117
|
-
|
|
118
|
-
if cmd:
|
|
119
|
-
new_args = new_args[len(cmd):]
|
|
120
|
-
|
|
121
|
-
return (state, new_args)
|
|
122
|
-
|
|
123
|
-
def validate(self, required: RequiredState = None, pg_required: RequiredState = None, app_required: RequiredState = None):
|
|
124
|
-
if not pg_required and not app_required:
|
|
125
|
-
if required == RequiredState.CLUSTER:
|
|
126
|
-
if not self.namespace or not self.sts:
|
|
127
|
-
if self.in_repl:
|
|
128
|
-
log2('cd to a cluster first.')
|
|
129
|
-
else:
|
|
130
|
-
log2('* cluster is missing.')
|
|
131
|
-
log2()
|
|
132
|
-
display_help()
|
|
133
|
-
|
|
134
|
-
return False
|
|
135
|
-
elif required == RequiredState.POD:
|
|
136
|
-
if not self.namespace or not self.pod:
|
|
137
|
-
if self.in_repl:
|
|
138
|
-
log2('cd to a pod first.')
|
|
139
|
-
else:
|
|
140
|
-
log2('* Pod is missing.')
|
|
141
|
-
log2()
|
|
142
|
-
display_help()
|
|
143
|
-
|
|
144
|
-
return False
|
|
145
|
-
elif required == RequiredState.CLUSTER_OR_POD:
|
|
146
|
-
if not self.namespace or not self.sts and not self.pod:
|
|
147
|
-
if self.in_repl:
|
|
148
|
-
log2('cd to a cluster first.')
|
|
149
|
-
else:
|
|
150
|
-
log2('* cluster or pod is missing.')
|
|
151
|
-
log2()
|
|
152
|
-
display_help()
|
|
153
|
-
|
|
154
|
-
return False
|
|
155
|
-
elif required == RequiredState.NAMESPACE:
|
|
156
|
-
if not self.namespace:
|
|
157
|
-
if self.in_repl:
|
|
158
|
-
log2('Namespace is required.')
|
|
159
|
-
else:
|
|
160
|
-
log2('* namespace is missing.')
|
|
161
|
-
log2()
|
|
162
|
-
display_help()
|
|
163
|
-
|
|
164
|
-
return False
|
|
165
|
-
|
|
166
|
-
if pg_required == RequiredState.PG_DATABASE:
|
|
167
|
-
pg = PostgresSession(self.namespace, self.pg_path)
|
|
168
|
-
if not pg.db:
|
|
169
|
-
if self.in_repl:
|
|
170
|
-
log2('cd to a database first.')
|
|
171
|
-
else:
|
|
172
|
-
log2('* database is missing.')
|
|
173
|
-
log2()
|
|
174
|
-
display_help()
|
|
175
|
-
|
|
176
|
-
return False
|
|
177
|
-
|
|
178
|
-
if app_required == RequiredState.APP_APP and not self.app_app:
|
|
179
|
-
if self.in_repl:
|
|
180
|
-
log2('cd to an app first.')
|
|
181
|
-
else:
|
|
182
|
-
log2('* app is missing.')
|
|
183
|
-
log2()
|
|
184
|
-
display_help()
|
|
185
|
-
|
|
186
|
-
return False
|
|
187
|
-
|
|
188
|
-
return True
|
|
189
|
-
|
|
190
|
-
def user_pass(self, secret_path = 'cql.secret'):
|
|
191
|
-
return Secrets.get_user_pass(self.pod if self.pod else self.sts, self.namespace, secret_path=secret_path)
|
|
192
|
-
|
|
193
|
-
def enter_bash(self, bash_session: BashSession):
|
|
194
|
-
self.bash_session = bash_session
|
|
195
|
-
if self.device != ReplState.C:
|
|
196
|
-
self.device = ReplState.C
|
|
197
|
-
log2(f'Moved to {ReplState.C}: automatically. Will move back to {ReplState.P}: when you exit the bash session.')
|
|
198
|
-
|
|
199
|
-
def exit_bash(self):
|
|
200
|
-
if self.bash_session and self.bash_session.device:
|
|
201
|
-
self.device = self.bash_session.device
|
|
202
|
-
|
|
203
|
-
self.bash_session = None
|
|
204
|
-
|
|
205
|
-
def wait_log(self, msg: str):
|
|
206
|
-
if not self.wait_log_flag:
|
|
207
|
-
log2(msg)
|
|
208
|
-
self.wait_log_flag = True
|
|
209
|
-
|
|
210
|
-
def clear_wait_log_flag(self):
|
|
211
|
-
self.wait_log_flag = False
|
walker/sso/authn_ad.py
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import re
|
|
3
|
-
import requests
|
|
4
|
-
from urllib.parse import urlparse, parse_qs, unquote
|
|
5
|
-
|
|
6
|
-
from walker.sso.authenticator import Authenticator
|
|
7
|
-
|
|
8
|
-
from .idp_login import IdpLogin
|
|
9
|
-
from walker.config import Config
|
|
10
|
-
from walker.utils import log2
|
|
11
|
-
|
|
12
|
-
class AdAuthenticator(Authenticator):
|
|
13
|
-
# the singleton pattern
|
|
14
|
-
def __new__(cls, *args, **kwargs):
|
|
15
|
-
if not hasattr(cls, 'instance'): cls.instance = super(AdAuthenticator, cls).__new__(cls)
|
|
16
|
-
|
|
17
|
-
return cls.instance
|
|
18
|
-
|
|
19
|
-
def authenticate(self, idp_uri: str, app_host: str, username: str, password: str) -> IdpLogin:
|
|
20
|
-
# https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin
|
|
21
|
-
parsed_url = urlparse(idp_uri)
|
|
22
|
-
query_string = parsed_url.query
|
|
23
|
-
params = parse_qs(query_string)
|
|
24
|
-
state_token = params.get('state', [''])[0]
|
|
25
|
-
redirect_url = params.get('redirect_uri', [''])[0]
|
|
26
|
-
|
|
27
|
-
session = requests.Session()
|
|
28
|
-
r = session.get(idp_uri)
|
|
29
|
-
if Config().is_debug():
|
|
30
|
-
log2(f'{r.status_code} {idp_uri}')
|
|
31
|
-
# print(r.text)
|
|
32
|
-
|
|
33
|
-
# extract_config_object('$Config={"fShowPersistentCookiesWarning":false}\n//]]></script>')
|
|
34
|
-
config = self.extract_config_object(r.text)
|
|
35
|
-
|
|
36
|
-
login = f'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/login';
|
|
37
|
-
body = {
|
|
38
|
-
'login': username,
|
|
39
|
-
'LoginOptions': '3',
|
|
40
|
-
'passwd': password,
|
|
41
|
-
'ctx': config['sCtx'],
|
|
42
|
-
'hpgrequestid': config['sessionId'],
|
|
43
|
-
'flowToken': config['sFT']
|
|
44
|
-
}
|
|
45
|
-
# print(body)
|
|
46
|
-
r = session.post(login, data=body, headers={
|
|
47
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
48
|
-
})
|
|
49
|
-
if Config().is_debug():
|
|
50
|
-
log2(f'{r.status_code} {login}')
|
|
51
|
-
# print(r.text)
|
|
52
|
-
|
|
53
|
-
config = self.extract_config_object(r.text)
|
|
54
|
-
|
|
55
|
-
kmsi = 'https://login.microsoftonline.com/kmsi'
|
|
56
|
-
body = {
|
|
57
|
-
'LoginOptions': '1',
|
|
58
|
-
'ctx': config['sCtx'],
|
|
59
|
-
'hpgrequestid': config['sessionId'],
|
|
60
|
-
'flowToken': config['sFT'],
|
|
61
|
-
}
|
|
62
|
-
r = session.post(kmsi, data=body, headers={
|
|
63
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
64
|
-
})
|
|
65
|
-
if Config().is_debug():
|
|
66
|
-
log2(f'{r.status_code} {kmsi}')
|
|
67
|
-
|
|
68
|
-
id_token = None
|
|
69
|
-
if (config := self.extract_config_object(r.text)):
|
|
70
|
-
if 'strServiceExceptionMessage' in config:
|
|
71
|
-
log2(config['strServiceExceptionMessage'])
|
|
72
|
-
else:
|
|
73
|
-
log2('Unknown err.')
|
|
74
|
-
|
|
75
|
-
return None
|
|
76
|
-
|
|
77
|
-
id_token = self.extract(r.text, r'.*name=\"id_token\" value=\"(.*?)\".*')
|
|
78
|
-
|
|
79
|
-
return IdpLogin(redirect_url, id_token, state_token, username, session=session)
|
|
80
|
-
|
|
81
|
-
def extract_config_object(self, text: str):
|
|
82
|
-
for line in text.split('\n'):
|
|
83
|
-
groups = re.match(r'.*\$Config=\s*(\{.*)', line)
|
|
84
|
-
if groups:
|
|
85
|
-
js = groups[1].replace(';', '')
|
|
86
|
-
config = json.loads(js)
|
|
87
|
-
|
|
88
|
-
# print("* sessionId =", config['sessionId'])
|
|
89
|
-
# print("* ctx =", config['sCtx'])
|
|
90
|
-
# print("* flowToken =", config['sFT'])
|
|
91
|
-
|
|
92
|
-
return config
|
|
93
|
-
|
|
94
|
-
return None
|
walker/sso/idp.py
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import getpass
|
|
2
|
-
import os
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from dotenv import load_dotenv
|
|
5
|
-
from urllib.parse import urlparse, parse_qs
|
|
6
|
-
|
|
7
|
-
from walker.sso import authn_okta
|
|
8
|
-
from walker.sso.authenticator import Authenticator
|
|
9
|
-
from walker.sso.authn_ad import AdAuthenticator
|
|
10
|
-
from walker.sso.sso_config import SsoConfig
|
|
11
|
-
|
|
12
|
-
from .idp_login import IdpLogin
|
|
13
|
-
from walker.config import Config
|
|
14
|
-
from walker.k8s_utils.kube_context import KubeContext
|
|
15
|
-
from walker.utils import log, log2
|
|
16
|
-
|
|
17
|
-
class Idp:
|
|
18
|
-
ctrl_c_entered = False
|
|
19
|
-
|
|
20
|
-
def __init__(self):
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
|
-
def parse_idp_uri(idp_uri: str):
|
|
24
|
-
parsed_url = urlparse(idp_uri)
|
|
25
|
-
query_string = parsed_url.query
|
|
26
|
-
params = parse_qs(query_string)
|
|
27
|
-
state_token = params.get('state', [''])[0]
|
|
28
|
-
redirect_url = params.get('redirect_uri', [''])[0]
|
|
29
|
-
|
|
30
|
-
return IdpLogin(app_login_url=redirect_url, id=None, state=state_token)
|
|
31
|
-
|
|
32
|
-
def login(app_host: str, username: str = None, forced = False, use_cached = True) -> IdpLogin:
|
|
33
|
-
idp_uri = SsoConfig().find_idp_uri(username, app_host, app_host)
|
|
34
|
-
|
|
35
|
-
if use_cached:
|
|
36
|
-
if idp_token := os.getenv('IDP_TOKEN'):
|
|
37
|
-
l0: IdpLogin = IdpLogin.deser(idp_token)
|
|
38
|
-
l1: IdpLogin = Idp.parse_idp_uri(idp_uri)
|
|
39
|
-
if l0.app_login_url == l1.app_login_url:
|
|
40
|
-
if l0.state != 'EMPTY':
|
|
41
|
-
return l0
|
|
42
|
-
|
|
43
|
-
l0.state = l1.state
|
|
44
|
-
|
|
45
|
-
return l0
|
|
46
|
-
|
|
47
|
-
return Idp.with_creds(app_host, idp_uri, forced, username=username)
|
|
48
|
-
|
|
49
|
-
def with_creds(app_host: str, idp_uri: str, forced: bool, username: str = None) -> IdpLogin:
|
|
50
|
-
idp = urlparse(idp_uri).hostname
|
|
51
|
-
|
|
52
|
-
def resolve_authn():
|
|
53
|
-
if 'okta' in idp.lower():
|
|
54
|
-
return authn_okta.OktaAuthenticator()
|
|
55
|
-
elif 'microsoftonline' in idp.lower():
|
|
56
|
-
return AdAuthenticator()
|
|
57
|
-
else:
|
|
58
|
-
log2(f'{idp} is not supported; only okta and ad are supported.')
|
|
59
|
-
|
|
60
|
-
return None
|
|
61
|
-
|
|
62
|
-
authn: Authenticator = resolve_authn()
|
|
63
|
-
if not authn:
|
|
64
|
-
return None
|
|
65
|
-
|
|
66
|
-
okta = idp.upper().split('.')[0]
|
|
67
|
-
dir = f'{Path.home()}/.kaqing'
|
|
68
|
-
env_f = f'{dir}/.credentials'
|
|
69
|
-
load_dotenv(dotenv_path=env_f)
|
|
70
|
-
|
|
71
|
-
# c3energy.okta.com login:
|
|
72
|
-
# Password:
|
|
73
|
-
# username = None
|
|
74
|
-
if username:
|
|
75
|
-
log(f'{idp} login: {username}')
|
|
76
|
-
|
|
77
|
-
while not username or Idp.ctrl_c_entered:
|
|
78
|
-
if Idp.ctrl_c_entered:
|
|
79
|
-
Idp.ctrl_c_entered = False
|
|
80
|
-
|
|
81
|
-
default_user = os.getenv(f'{okta}_USERNAME')
|
|
82
|
-
if default_user:
|
|
83
|
-
if forced:
|
|
84
|
-
username = default_user
|
|
85
|
-
else:
|
|
86
|
-
username = input(f'{idp} login(default {default_user}): ') or default_user
|
|
87
|
-
else:
|
|
88
|
-
username = input(f'{idp} login: ')
|
|
89
|
-
|
|
90
|
-
idp2_uri = SsoConfig().find_idp_uri(username, app_host, app_host)
|
|
91
|
-
if idp != (idp2 := urlparse(idp2_uri).hostname):
|
|
92
|
-
log(f'Switched to {idp2}.')
|
|
93
|
-
idp = idp2
|
|
94
|
-
log(f'{idp} login: {username}')
|
|
95
|
-
authn = resolve_authn()
|
|
96
|
-
idp_uri = idp2_uri
|
|
97
|
-
|
|
98
|
-
password = None
|
|
99
|
-
while not password or Idp.ctrl_c_entered:
|
|
100
|
-
if Idp.ctrl_c_entered:
|
|
101
|
-
Idp.ctrl_c_entered = False
|
|
102
|
-
|
|
103
|
-
default_pass = os.getenv(f'{okta}_PASSWORD')
|
|
104
|
-
if default_pass:
|
|
105
|
-
if forced:
|
|
106
|
-
password = default_pass
|
|
107
|
-
else:
|
|
108
|
-
password = getpass.getpass(f'Password(default ********): ') or default_pass
|
|
109
|
-
else:
|
|
110
|
-
password = getpass.getpass(f'Password: ')
|
|
111
|
-
|
|
112
|
-
if username and password:
|
|
113
|
-
r: IdpLogin = None
|
|
114
|
-
try:
|
|
115
|
-
r = authn.authenticate(idp_uri, app_host, username, password)
|
|
116
|
-
|
|
117
|
-
return r
|
|
118
|
-
finally:
|
|
119
|
-
if r and Config().get('app.login.cache-creds', True):
|
|
120
|
-
updated = []
|
|
121
|
-
if os.path.exists(env_f):
|
|
122
|
-
with open(env_f, 'r') as file:
|
|
123
|
-
try:
|
|
124
|
-
file_content = file.read()
|
|
125
|
-
for l in file_content.split('\n'):
|
|
126
|
-
tks = l.split('=')
|
|
127
|
-
key = tks[0]
|
|
128
|
-
value = tks[1] if len(tks) > 1 else ''
|
|
129
|
-
if key == f'{okta}_USERNAME':
|
|
130
|
-
value = username
|
|
131
|
-
elif key == f'{okta}_PASSWORD' and not KubeContext.in_cluster():
|
|
132
|
-
# do not store password to the .credentials file when in Kubernetes pod
|
|
133
|
-
value = password
|
|
134
|
-
updated.append(f'{key}={value}')
|
|
135
|
-
except:
|
|
136
|
-
updated = None
|
|
137
|
-
log2('Update failed')
|
|
138
|
-
else:
|
|
139
|
-
updated.append(f'{okta}_USERNAME={username}')
|
|
140
|
-
if not KubeContext.in_cluster():
|
|
141
|
-
# do not store password to the .credentials file when in Kubernetes pod
|
|
142
|
-
updated.append(f'{okta}_PASSWORD={password}')
|
|
143
|
-
|
|
144
|
-
if updated:
|
|
145
|
-
if not os.path.exists(env_f):
|
|
146
|
-
os.makedirs(dir, exist_ok=True)
|
|
147
|
-
with open(env_f, "w") as file:
|
|
148
|
-
file.write('\n'.join(updated))
|
|
149
|
-
|
|
150
|
-
return None
|
walker/sso/idp_login.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import json
|
|
3
|
-
import requests
|
|
4
|
-
|
|
5
|
-
class IdpLogin:
|
|
6
|
-
def __init__(self, app_login_url: str, id: str, state: str, user: str = None, session: requests.Session = None):
|
|
7
|
-
self.app_login_url = app_login_url
|
|
8
|
-
self.id = id
|
|
9
|
-
self.state = state
|
|
10
|
-
self.user = user
|
|
11
|
-
self.session = session
|
|
12
|
-
|
|
13
|
-
def deser(idp_token: str):
|
|
14
|
-
j = json.loads(base64.b64decode(idp_token.encode('utf-8')))
|
|
15
|
-
|
|
16
|
-
return IdpLogin(j['r'], j['id'], j['state'])
|
|
17
|
-
|
|
18
|
-
def ser(self):
|
|
19
|
-
return base64.b64encode(json.dumps({
|
|
20
|
-
'r': self.app_login_url,
|
|
21
|
-
'id': self.id,
|
|
22
|
-
'state': self.state
|
|
23
|
-
}).encode('utf-8')).decode('utf-8')
|
|
24
|
-
|
|
25
|
-
def shell_user(self):
|
|
26
|
-
if not self.user:
|
|
27
|
-
return None
|
|
28
|
-
|
|
29
|
-
return self.user.split('@')[0].replace('.', '')
|
walker/sso/sso_config.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
import re
|
|
5
|
-
import requests
|
|
6
|
-
|
|
7
|
-
from walker.config import Config
|
|
8
|
-
|
|
9
|
-
class SsoConfig:
|
|
10
|
-
# the singleton pattern
|
|
11
|
-
def __new__(cls, *args, **kwargs):
|
|
12
|
-
if not hasattr(cls, 'instance'): cls.instance = super(SsoConfig, cls).__new__(cls)
|
|
13
|
-
|
|
14
|
-
return cls.instance
|
|
15
|
-
|
|
16
|
-
def __init__(self):
|
|
17
|
-
if not hasattr(self, 'config'):
|
|
18
|
-
self.config = Config().get('idps', {
|
|
19
|
-
|
|
20
|
-
})
|
|
21
|
-
# print(self.config)
|
|
22
|
-
|
|
23
|
-
def find_idp_uri(self, user_email: str, client_id: str, app_host: str):
|
|
24
|
-
default = 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY'
|
|
25
|
-
|
|
26
|
-
def rpl(uri: str):
|
|
27
|
-
return uri.replace('{client_id}', client_id).replace('{host}', app_host).replace('{nonce}', SsoConfig.generate_oauth_nonce())
|
|
28
|
-
|
|
29
|
-
if not user_email:
|
|
30
|
-
return rpl(default)
|
|
31
|
-
|
|
32
|
-
for idp_name, conf in self.config.items():
|
|
33
|
-
if 'email-pattern' in conf:
|
|
34
|
-
groups = re.match(conf['email-pattern'], user_email)
|
|
35
|
-
if groups:
|
|
36
|
-
return rpl(conf['uri'])
|
|
37
|
-
|
|
38
|
-
return rpl(default)
|
|
39
|
-
|
|
40
|
-
def generate_oauth_nonce():
|
|
41
|
-
random_bytes = os.urandom(32)
|
|
42
|
-
nonce = base64.urlsafe_b64encode(random_bytes).decode('utf-8')
|
|
43
|
-
nonce = nonce.rstrip('=')
|
|
44
|
-
|
|
45
|
-
return nonce
|