kaqing 1.98.86__py3-none-any.whl → 1.98.88__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kaqing might be problematic. Click here for more details.
- adam/app_session.py +1 -1
- adam/batch.py +15 -15
- adam/commands/{frontend → deploy}/code_start.py +1 -1
- adam/commands/{frontend → deploy}/code_stop.py +1 -1
- adam/commands/{frontend/setup.py → deploy/deploy.py} +11 -10
- adam/commands/{frontend/setup_frontend.py → deploy/deploy_frontend.py} +11 -15
- adam/commands/deploy/deploy_pod.py +68 -0
- adam/commands/deploy/deploy_utils.py +26 -0
- adam/commands/{frontend/teardown.py → deploy/undeploy.py} +12 -11
- adam/commands/{frontend/teardown_frontend.py → deploy/undeploy_frontend.py} +9 -8
- adam/commands/deploy/undeploy_pod.py +54 -0
- adam/commands/postgres/postgres_session.py +61 -54
- adam/commands/user_entry.py +1 -1
- adam/embedded_params.py +1 -1
- adam/k8s_utils/pods.py +21 -6
- adam/k8s_utils/service_accounts.py +169 -0
- adam/repl_commands.py +10 -8
- adam/sso/authenticator.py +1 -1
- adam/sso/authn_ad.py +4 -1
- adam/sso/authn_okta.py +5 -1
- adam/sso/idp.py +3 -3
- adam/version.py +1 -1
- {kaqing-1.98.86.dist-info → kaqing-1.98.88.dist-info}/METADATA +1 -1
- {kaqing-1.98.86.dist-info → kaqing-1.98.88.dist-info}/RECORD +29 -25
- /adam/commands/{frontend → deploy}/__init__.py +0 -0
- /adam/commands/{frontend → deploy}/code_utils.py +0 -0
- {kaqing-1.98.86.dist-info → kaqing-1.98.88.dist-info}/WHEEL +0 -0
- {kaqing-1.98.86.dist-info → kaqing-1.98.88.dist-info}/entry_points.txt +0 -0
- {kaqing-1.98.86.dist-info → kaqing-1.98.88.dist-info}/top_level.txt +0 -0
adam/app_session.py
CHANGED
|
@@ -88,7 +88,7 @@ class AppSession:
|
|
|
88
88
|
if not forced and self.app_login:
|
|
89
89
|
return self.app_login
|
|
90
90
|
|
|
91
|
-
idp_login: IdpLogin = Idp.login(self.host, idp_uri=idp_uri, forced=forced, use_token_from_env=use_token_from_env, use_cached_creds=use_cached_creds)
|
|
91
|
+
idp_login: IdpLogin = Idp.login(self.host, idp_uri=idp_uri, forced=forced, use_token_from_env=use_token_from_env, use_cached_creds=use_cached_creds, verify=False)
|
|
92
92
|
if not idp_login:
|
|
93
93
|
log2(f"Invalid username/password.")
|
|
94
94
|
|
adam/batch.py
CHANGED
|
@@ -6,8 +6,8 @@ from adam.commands.cp import ClipboardCopy, CopyCommandHelper
|
|
|
6
6
|
from adam.commands.command import Command
|
|
7
7
|
from adam.commands.command_helpers import ClusterCommandHelper, ClusterOrPodCommandHelper, PodCommandHelper
|
|
8
8
|
from adam.commands.cqlsh import CqlCommandHelper, Cqlsh
|
|
9
|
-
from adam.commands.
|
|
10
|
-
from adam.commands.
|
|
9
|
+
from adam.commands.deploy.deploy import Deploy, DeployCommandHelper
|
|
10
|
+
from adam.commands.deploy.undeploy import Undeploy, UndeployCommandHelper
|
|
11
11
|
from adam.commands.issues import Issues
|
|
12
12
|
from adam.commands.login import Login
|
|
13
13
|
from adam.commands.logs import Logs
|
|
@@ -77,6 +77,16 @@ def cql(kubeconfig: str, config: str, param: list[str], cluster: str, namespace:
|
|
|
77
77
|
run_command(Cqlsh(), kubeconfig, config, param, cluster, namespace, pod, extra_args)
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=DeployCommandHelper, help='Setup.')
|
|
81
|
+
@click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
|
|
82
|
+
@click.option('--config', default='params.yaml', metavar='path', help='path to kaqing parameters file')
|
|
83
|
+
@click.option('--param', '-v', multiple=True, metavar='<key>=<value>', help='parameter override')
|
|
84
|
+
@click.option('--namespace', '-n', required=False, metavar='namespace', help='Kubernetes namespace')
|
|
85
|
+
@click.argument('extra_args', nargs=-1, metavar='<pod>', type=click.UNPROCESSED)
|
|
86
|
+
def deploy(kubeconfig: str, config: str, param: list[str], namespace: str, extra_args):
|
|
87
|
+
run_command(Deploy(), kubeconfig, config, param, None, namespace, None, extra_args)
|
|
88
|
+
|
|
89
|
+
|
|
80
90
|
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=ClusterOrPodCommandHelper, help="Print Qing's issues.")
|
|
81
91
|
@click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
|
|
82
92
|
@click.option('--config', default='params.yaml', metavar='path', help='path to kaqing parameters file')
|
|
@@ -229,16 +239,6 @@ def rollout(kubeconfig: str, config: str, param: list[str], cluster: str, namesp
|
|
|
229
239
|
run_command(RollOut(), kubeconfig, config, param, cluster, namespace, None, extra_args)
|
|
230
240
|
|
|
231
241
|
|
|
232
|
-
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=SetupCommandHelper, help='Setup.')
|
|
233
|
-
@click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
|
|
234
|
-
@click.option('--config', default='params.yaml', metavar='path', help='path to kaqing parameters file')
|
|
235
|
-
@click.option('--param', '-v', multiple=True, metavar='<key>=<value>', help='parameter override')
|
|
236
|
-
@click.option('--namespace', '-n', required=False, metavar='namespace', help='Kubernetes namespace')
|
|
237
|
-
@click.argument('extra_args', nargs=-1, metavar='<pod>', type=click.UNPROCESSED)
|
|
238
|
-
def setup(kubeconfig: str, config: str, param: list[str], namespace: str, extra_args):
|
|
239
|
-
run_command(Setup(), kubeconfig, config, param, None, namespace, None, extra_args)
|
|
240
|
-
|
|
241
|
-
|
|
242
242
|
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=ShowCommandHelper, help='Show configuration or kubectl commands.')
|
|
243
243
|
@click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
|
|
244
244
|
@click.option('--config', default='params.yaml', metavar='path', help='path to kaqing parameters file')
|
|
@@ -251,14 +251,14 @@ def show(kubeconfig: str, config: str, param: list[str], cluster: str, namespace
|
|
|
251
251
|
run_command(Show(), kubeconfig, config, param, cluster, namespace, pod, extra_args)
|
|
252
252
|
|
|
253
253
|
|
|
254
|
-
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=
|
|
254
|
+
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=UndeployCommandHelper, help='Undeploy.')
|
|
255
255
|
@click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
|
|
256
256
|
@click.option('--config', default='params.yaml', metavar='path', help='path to kaqing parameters file')
|
|
257
257
|
@click.option('--param', '-v', multiple=True, metavar='<key>=<value>', help='parameter override')
|
|
258
258
|
@click.option('--namespace', '-n', required=False, metavar='namespace', help='Kubernetes namespace')
|
|
259
259
|
@click.argument('extra_args', nargs=-1, metavar='<pod>', type=click.UNPROCESSED)
|
|
260
|
-
def
|
|
261
|
-
run_command(
|
|
260
|
+
def undeploy(kubeconfig: str, config: str, param: list[str], namespace: str, extra_args):
|
|
261
|
+
run_command(Undeploy(), kubeconfig, config, param, None, namespace, None, extra_args)
|
|
262
262
|
|
|
263
263
|
|
|
264
264
|
@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=PodCommandHelper, help='Get cassandra log.')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from adam.commands.command import Command
|
|
2
|
-
from adam.commands.
|
|
2
|
+
from adam.commands.deploy.code_utils import start_user_code, stop_user_codes
|
|
3
3
|
from adam.repl_state import ReplState, RequiredState
|
|
4
4
|
from adam.utils import log2
|
|
5
5
|
|
|
@@ -2,17 +2,18 @@ import click
|
|
|
2
2
|
|
|
3
3
|
from adam.commands.command import Command
|
|
4
4
|
from adam.commands.command_helpers import ClusterCommandHelper
|
|
5
|
-
from .
|
|
5
|
+
from adam.commands.deploy.deploy_pod import DeployPod
|
|
6
|
+
from .deploy_frontend import DeployFrontend
|
|
6
7
|
from adam.repl_state import ReplState
|
|
7
8
|
from adam.utils import lines_to_tabular, log, log2
|
|
8
9
|
|
|
9
|
-
class
|
|
10
|
-
COMMAND = '
|
|
10
|
+
class Deploy(Command):
|
|
11
|
+
COMMAND = 'deploy'
|
|
11
12
|
reaper_login = None
|
|
12
13
|
|
|
13
14
|
# the singleton pattern
|
|
14
15
|
def __new__(cls, *args, **kwargs):
|
|
15
|
-
if not hasattr(cls, 'instance'): cls.instance = super(
|
|
16
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Deploy, cls).__new__(cls)
|
|
16
17
|
|
|
17
18
|
return cls.instance
|
|
18
19
|
|
|
@@ -20,7 +21,7 @@ class Setup(Command):
|
|
|
20
21
|
super().__init__(successor)
|
|
21
22
|
|
|
22
23
|
def command(self):
|
|
23
|
-
return
|
|
24
|
+
return Deploy.COMMAND
|
|
24
25
|
|
|
25
26
|
def run(self, cmd: str, state: ReplState):
|
|
26
27
|
if not(args := self.args(cmd)):
|
|
@@ -29,18 +30,18 @@ class Setup(Command):
|
|
|
29
30
|
state, args = self.apply_state(args, state)
|
|
30
31
|
|
|
31
32
|
if state.in_repl:
|
|
32
|
-
log(lines_to_tabular([c.help(ReplState()) for c in
|
|
33
|
+
log(lines_to_tabular([c.help(ReplState()) for c in Deploy.cmd_list()], separator=':'))
|
|
33
34
|
|
|
34
35
|
return 'command-missing'
|
|
35
36
|
else:
|
|
36
37
|
# head with the Chain of Responsibility pattern
|
|
37
|
-
cmds = Command.chain(
|
|
38
|
+
cmds = Command.chain(Deploy.cmd_list())
|
|
38
39
|
if not cmds.run(cmd, state):
|
|
39
40
|
log2('* Command is missing.')
|
|
40
41
|
Command.display_help()
|
|
41
42
|
|
|
42
43
|
def cmd_list():
|
|
43
|
-
return [
|
|
44
|
+
return [DeployFrontend(), DeployPod()]
|
|
44
45
|
|
|
45
46
|
def completion(self, state: ReplState):
|
|
46
47
|
if state.sts:
|
|
@@ -51,10 +52,10 @@ class Setup(Command):
|
|
|
51
52
|
def help(self, _: ReplState):
|
|
52
53
|
return None
|
|
53
54
|
|
|
54
|
-
class
|
|
55
|
+
class DeployCommandHelper(click.Command):
|
|
55
56
|
def get_help(self, ctx: click.Context):
|
|
56
57
|
log(super().get_help(ctx))
|
|
57
58
|
log()
|
|
58
59
|
log('Sub-Commands:')
|
|
59
60
|
|
|
60
|
-
log(lines_to_tabular([c.help(ReplState()).replace(f'{
|
|
61
|
+
log(lines_to_tabular([c.help(ReplState()).replace(f'{Deploy.COMMAND} ', ' ', 1) for c in Deploy.cmd_list()], separator=':'))
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
from adam.app_session import AppSession
|
|
2
2
|
from adam.commands.command import Command
|
|
3
|
+
from adam.commands.deploy.deploy_utils import deploy_frontend
|
|
4
|
+
from adam.config import Config
|
|
3
5
|
from adam.k8s_utils.ingresses import Ingresses
|
|
4
6
|
from adam.k8s_utils.services import Services
|
|
5
7
|
from adam.repl_state import ReplState, RequiredState
|
|
6
8
|
from adam.utils import log2
|
|
7
9
|
|
|
8
|
-
class
|
|
9
|
-
COMMAND = '
|
|
10
|
+
class DeployFrontend(Command):
|
|
11
|
+
COMMAND = 'deploy frontend'
|
|
10
12
|
|
|
11
13
|
# the singleton pattern
|
|
12
14
|
def __new__(cls, *args, **kwargs):
|
|
13
|
-
if not hasattr(cls, 'instance'): cls.instance = super(
|
|
15
|
+
if not hasattr(cls, 'instance'): cls.instance = super(DeployFrontend, cls).__new__(cls)
|
|
14
16
|
|
|
15
17
|
return cls.instance
|
|
16
18
|
|
|
@@ -18,7 +20,7 @@ class SetupFrontend(Command):
|
|
|
18
20
|
super().__init__(successor)
|
|
19
21
|
|
|
20
22
|
def command(self):
|
|
21
|
-
return
|
|
23
|
+
return DeployFrontend.COMMAND
|
|
22
24
|
|
|
23
25
|
def required(self):
|
|
24
26
|
return RequiredState.NAMESPACE
|
|
@@ -33,19 +35,13 @@ class SetupFrontend(Command):
|
|
|
33
35
|
|
|
34
36
|
log2('This will support c3/c3 only for demo.')
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
pod_name = Config().get('pod.name', 'ops')
|
|
39
|
+
label_selector = Config().get('pod.label-selector', 'run=ops')
|
|
37
40
|
try:
|
|
38
|
-
|
|
39
|
-
port = 7678
|
|
40
|
-
Services.create_service(name, state.namespace, port, {"run": "ops"})
|
|
41
|
-
Ingresses.create_ingress(name, state.namespace, app_session.host, '/c3/c3/ops($|/)', port, annotations={
|
|
42
|
-
'kubernetes.io/ingress.class': 'nginx',
|
|
43
|
-
'nginx.ingress.kubernetes.io/use-regex': 'true',
|
|
44
|
-
'nginx.ingress.kubernetes.io/rewrite-target': '/'
|
|
45
|
-
})
|
|
41
|
+
deploy_frontend(pod_name, state.namespace, label_selector)
|
|
46
42
|
except Exception as e:
|
|
47
43
|
if e.status == 409:
|
|
48
|
-
log2(f"Error: '{
|
|
44
|
+
log2(f"Error: '{pod_name}' already exists in namespace '{state.namespace}'.")
|
|
49
45
|
else:
|
|
50
46
|
log2(f"Error creating ingress or service: {e}")
|
|
51
47
|
|
|
@@ -55,4 +51,4 @@ class SetupFrontend(Command):
|
|
|
55
51
|
return super().completion(state)
|
|
56
52
|
|
|
57
53
|
def help(self, _: ReplState):
|
|
58
|
-
return f'{
|
|
54
|
+
return f'{DeployFrontend.COMMAND}\t deploy frontend'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from kubernetes import client
|
|
2
|
+
|
|
3
|
+
from adam.commands.command import Command
|
|
4
|
+
from adam.commands.deploy.deploy_utils import deploy_frontend, gen_labels
|
|
5
|
+
from adam.config import Config
|
|
6
|
+
from adam.k8s_utils.ingresses import Ingresses
|
|
7
|
+
from adam.k8s_utils.kube_context import KubeContext
|
|
8
|
+
from adam.k8s_utils.pods import Pods
|
|
9
|
+
from adam.k8s_utils.service_accounts import ServiceAccounts
|
|
10
|
+
from adam.k8s_utils.services import Services
|
|
11
|
+
from adam.repl_state import ReplState, RequiredState
|
|
12
|
+
from adam.utils import log2
|
|
13
|
+
|
|
14
|
+
class DeployPod(Command):
|
|
15
|
+
COMMAND = 'deploy pod'
|
|
16
|
+
|
|
17
|
+
# the singleton pattern
|
|
18
|
+
def __new__(cls, *args, **kwargs):
|
|
19
|
+
if not hasattr(cls, 'instance'): cls.instance = super(DeployPod, cls).__new__(cls)
|
|
20
|
+
|
|
21
|
+
return cls.instance
|
|
22
|
+
|
|
23
|
+
def __init__(self, successor: Command=None):
|
|
24
|
+
super().__init__(successor)
|
|
25
|
+
|
|
26
|
+
def command(self):
|
|
27
|
+
return DeployPod.COMMAND
|
|
28
|
+
|
|
29
|
+
def required(self):
|
|
30
|
+
return RequiredState.NAMESPACE
|
|
31
|
+
|
|
32
|
+
def run(self, cmd: str, state: ReplState):
|
|
33
|
+
if not(args := self.args(cmd)):
|
|
34
|
+
return super().run(cmd, state)
|
|
35
|
+
|
|
36
|
+
state, args = self.apply_state(args, state)
|
|
37
|
+
if not self.validate_state(state):
|
|
38
|
+
return state
|
|
39
|
+
|
|
40
|
+
if KubeContext.in_cluster():
|
|
41
|
+
log2('This is doable only from outside of the Kubernetes cluster.')
|
|
42
|
+
return state
|
|
43
|
+
|
|
44
|
+
sa_name = Config().get('pod.sa.name', 'ops')
|
|
45
|
+
sa_proto = Config().get('pod.sa.proto', 'c3')
|
|
46
|
+
additional_cluster_roles = Config().get('pod.sa.additional-cluster-roles', 'c3aiops-k8ssandra-operator').split(',')
|
|
47
|
+
label_selector = Config().get('pod.label-selector', 'run=ops')
|
|
48
|
+
labels = gen_labels(label_selector)
|
|
49
|
+
ServiceAccounts.replicate(sa_name, state.namespace, sa_proto, labels=labels, add_cluster_roles=additional_cluster_roles)
|
|
50
|
+
|
|
51
|
+
pod_name = Config().get('pod.name', 'ops')
|
|
52
|
+
image = Config().get('pod.image', 'seanahnsf/kaqing')
|
|
53
|
+
security_context = client.V1SecurityContext(
|
|
54
|
+
capabilities=client.V1Capabilities(
|
|
55
|
+
add=["SYS_PTRACE"]
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
Pods.create(state.namespace, pod_name, image, env={'NAMESPACE': state.namespace}, container_security_context=security_context, labels=labels, sa_name=sa_name)
|
|
59
|
+
|
|
60
|
+
deploy_frontend(pod_name, state.namespace, label_selector)
|
|
61
|
+
|
|
62
|
+
return state
|
|
63
|
+
|
|
64
|
+
def completion(self, state: ReplState):
|
|
65
|
+
return super().completion(state)
|
|
66
|
+
|
|
67
|
+
def help(self, _: ReplState):
|
|
68
|
+
return f'{DeployPod.COMMAND}\t deploy pod'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from adam.app_session import AppSession
|
|
2
|
+
from adam.k8s_utils.ingresses import Ingresses
|
|
3
|
+
from adam.k8s_utils.services import Services
|
|
4
|
+
from adam.utils import log2
|
|
5
|
+
|
|
6
|
+
def deploy_frontend(name: str, namespace: str, label_selector: str):
|
|
7
|
+
app_session: AppSession = AppSession.create('c3', 'c3', namespace)
|
|
8
|
+
port = 7678
|
|
9
|
+
labels = gen_labels(label_selector)
|
|
10
|
+
Services.create_service(name, namespace, port, labels, labels=labels)
|
|
11
|
+
# Services.create_service(name, namespace, port, {"run": "ops"})
|
|
12
|
+
Ingresses.create_ingress(name, namespace, app_session.host, '/c3/c3/ops($|/)', port, annotations={
|
|
13
|
+
'kubernetes.io/ingress.class': 'nginx',
|
|
14
|
+
'nginx.ingress.kubernetes.io/use-regex': 'true',
|
|
15
|
+
'nginx.ingress.kubernetes.io/rewrite-target': '/'
|
|
16
|
+
}, labels=labels)
|
|
17
|
+
|
|
18
|
+
log2(f'Ops pod is available at https://{app_session.host}/c3/c3/ops')
|
|
19
|
+
|
|
20
|
+
def undeploy_frontend(namespace: str, label_selector: str):
|
|
21
|
+
Ingresses.delete_ingresses(namespace, label_selector=label_selector)
|
|
22
|
+
Services.delete_services(namespace, label_selector=label_selector)
|
|
23
|
+
|
|
24
|
+
def gen_labels(label_selector: str):
|
|
25
|
+
kv = label_selector.split('=')
|
|
26
|
+
return {kv[0]: kv[1]}
|
|
@@ -2,18 +2,19 @@ import click
|
|
|
2
2
|
|
|
3
3
|
from adam.commands.command import Command
|
|
4
4
|
from adam.commands.command_helpers import ClusterCommandHelper
|
|
5
|
-
from adam.commands.
|
|
6
|
-
from .
|
|
5
|
+
from adam.commands.deploy.undeploy_frontend import UndeployFrontend
|
|
6
|
+
from adam.commands.deploy.undeploy_pod import UndeployPod
|
|
7
|
+
from .deploy_frontend import DeployFrontend
|
|
7
8
|
from adam.repl_state import ReplState
|
|
8
9
|
from adam.utils import lines_to_tabular, log, log2
|
|
9
10
|
|
|
10
|
-
class
|
|
11
|
-
COMMAND = '
|
|
11
|
+
class Undeploy(Command):
|
|
12
|
+
COMMAND = 'undeploy'
|
|
12
13
|
reaper_login = None
|
|
13
14
|
|
|
14
15
|
# the singleton pattern
|
|
15
16
|
def __new__(cls, *args, **kwargs):
|
|
16
|
-
if not hasattr(cls, 'instance'): cls.instance = super(
|
|
17
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Undeploy, cls).__new__(cls)
|
|
17
18
|
|
|
18
19
|
return cls.instance
|
|
19
20
|
|
|
@@ -21,7 +22,7 @@ class TearDown(Command):
|
|
|
21
22
|
super().__init__(successor)
|
|
22
23
|
|
|
23
24
|
def command(self):
|
|
24
|
-
return
|
|
25
|
+
return Undeploy.COMMAND
|
|
25
26
|
|
|
26
27
|
def run(self, cmd: str, state: ReplState):
|
|
27
28
|
if not(args := self.args(cmd)):
|
|
@@ -30,18 +31,18 @@ class TearDown(Command):
|
|
|
30
31
|
state, args = self.apply_state(args, state)
|
|
31
32
|
|
|
32
33
|
if state.in_repl:
|
|
33
|
-
log(lines_to_tabular([c.help(ReplState()) for c in
|
|
34
|
+
log(lines_to_tabular([c.help(ReplState()) for c in Undeploy.cmd_list()], separator=':'))
|
|
34
35
|
|
|
35
36
|
return 'command-missing'
|
|
36
37
|
else:
|
|
37
38
|
# head with the Chain of Responsibility pattern
|
|
38
|
-
cmds = Command.chain(
|
|
39
|
+
cmds = Command.chain(Undeploy.cmd_list())
|
|
39
40
|
if not cmds.run(cmd, state):
|
|
40
41
|
log2('* Command is missing.')
|
|
41
42
|
Command.display_help()
|
|
42
43
|
|
|
43
44
|
def cmd_list():
|
|
44
|
-
return [
|
|
45
|
+
return [UndeployFrontend(), UndeployPod()]
|
|
45
46
|
|
|
46
47
|
def completion(self, state: ReplState):
|
|
47
48
|
if state.sts:
|
|
@@ -52,10 +53,10 @@ class TearDown(Command):
|
|
|
52
53
|
def help(self, _: ReplState):
|
|
53
54
|
return None
|
|
54
55
|
|
|
55
|
-
class
|
|
56
|
+
class UndeployCommandHelper(click.Command):
|
|
56
57
|
def get_help(self, ctx: click.Context):
|
|
57
58
|
log(super().get_help(ctx))
|
|
58
59
|
log()
|
|
59
60
|
log('Sub-Commands:')
|
|
60
61
|
|
|
61
|
-
log(lines_to_tabular([c.help(ReplState()).replace(f'{
|
|
62
|
+
log(lines_to_tabular([c.help(ReplState()).replace(f'{Undeploy.COMMAND} ', ' ', 1) for c in Undeploy.cmd_list()], separator=':'))
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
from adam.commands.command import Command
|
|
2
|
+
from adam.commands.deploy.deploy_utils import undeploy_frontend
|
|
3
|
+
from adam.config import Config
|
|
2
4
|
from adam.k8s_utils.ingresses import Ingresses
|
|
3
5
|
from adam.k8s_utils.services import Services
|
|
4
6
|
from adam.repl_state import ReplState, RequiredState
|
|
5
7
|
|
|
6
|
-
class
|
|
7
|
-
COMMAND = '
|
|
8
|
+
class UndeployFrontend(Command):
|
|
9
|
+
COMMAND = 'undeploy frontend'
|
|
8
10
|
|
|
9
11
|
# the singleton pattern
|
|
10
12
|
def __new__(cls, *args, **kwargs):
|
|
11
|
-
if not hasattr(cls, 'instance'): cls.instance = super(
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(UndeployFrontend, cls).__new__(cls)
|
|
12
14
|
|
|
13
15
|
return cls.instance
|
|
14
16
|
|
|
@@ -16,7 +18,7 @@ class TearDownFrontend(Command):
|
|
|
16
18
|
super().__init__(successor)
|
|
17
19
|
|
|
18
20
|
def command(self):
|
|
19
|
-
return
|
|
21
|
+
return UndeployFrontend.COMMAND
|
|
20
22
|
|
|
21
23
|
def required(self):
|
|
22
24
|
return RequiredState.NAMESPACE
|
|
@@ -29,9 +31,8 @@ class TearDownFrontend(Command):
|
|
|
29
31
|
if not self.validate_state(state):
|
|
30
32
|
return state
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Services.delete_service(name, state.namespace)
|
|
34
|
+
label_selector = Config().get('pod.label-selector', 'run=ops')
|
|
35
|
+
undeploy_frontend(state.namespace, label_selector)
|
|
35
36
|
|
|
36
37
|
return state
|
|
37
38
|
|
|
@@ -39,4 +40,4 @@ class TearDownFrontend(Command):
|
|
|
39
40
|
return super().completion(state)
|
|
40
41
|
|
|
41
42
|
def help(self, _: ReplState):
|
|
42
|
-
return f'{
|
|
43
|
+
return f'{UndeployFrontend.COMMAND}\t undeploy frontend'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from adam.app_session import AppSession
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.commands.deploy.deploy_utils import deploy_frontend, undeploy_frontend
|
|
4
|
+
from adam.config import Config
|
|
5
|
+
from adam.k8s_utils.ingresses import Ingresses
|
|
6
|
+
from adam.k8s_utils.kube_context import KubeContext
|
|
7
|
+
from adam.k8s_utils.pods import Pods
|
|
8
|
+
from adam.k8s_utils.service_accounts import ServiceAccounts
|
|
9
|
+
from adam.k8s_utils.services import Services
|
|
10
|
+
from adam.repl_state import ReplState, RequiredState
|
|
11
|
+
from adam.utils import log2
|
|
12
|
+
|
|
13
|
+
class UndeployPod(Command):
|
|
14
|
+
COMMAND = 'undeploy pod'
|
|
15
|
+
|
|
16
|
+
# the singleton pattern
|
|
17
|
+
def __new__(cls, *args, **kwargs):
|
|
18
|
+
if not hasattr(cls, 'instance'): cls.instance = super(UndeployPod, 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 UndeployPod.COMMAND
|
|
27
|
+
|
|
28
|
+
def required(self):
|
|
29
|
+
return RequiredState.NAMESPACE
|
|
30
|
+
|
|
31
|
+
def run(self, cmd: str, state: ReplState):
|
|
32
|
+
if not(args := self.args(cmd)):
|
|
33
|
+
return super().run(cmd, state)
|
|
34
|
+
|
|
35
|
+
state, args = self.apply_state(args, state)
|
|
36
|
+
if not self.validate_state(state):
|
|
37
|
+
return state
|
|
38
|
+
|
|
39
|
+
if KubeContext.in_cluster():
|
|
40
|
+
log2('This is doable only from outside of the Kubernetes cluster.')
|
|
41
|
+
return state
|
|
42
|
+
|
|
43
|
+
label_selector = Config().get('pod.label-selector', 'run=ops')
|
|
44
|
+
undeploy_frontend(state.namespace, label_selector)
|
|
45
|
+
Pods.delete_with_selector(state.namespace, label_selector)
|
|
46
|
+
ServiceAccounts.delete(state.namespace, label_selector=label_selector)
|
|
47
|
+
|
|
48
|
+
return state
|
|
49
|
+
|
|
50
|
+
def completion(self, state: ReplState):
|
|
51
|
+
return super().completion(state)
|
|
52
|
+
|
|
53
|
+
def help(self, _: ReplState):
|
|
54
|
+
return f'{UndeployPod.COMMAND}\t undeploy pod'
|
|
@@ -93,23 +93,23 @@ class PostgresSession:
|
|
|
93
93
|
# template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
|
|
94
94
|
# | | | | | | | postgres=CTc/postgres
|
|
95
95
|
# (48 rows)
|
|
96
|
-
r
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
96
|
+
if r := self.run_sql('\l', show_out=False):
|
|
97
|
+
s = 0
|
|
98
|
+
for line in r.stdout.split('\n'):
|
|
99
|
+
line: str = line.strip(' \r')
|
|
100
|
+
if s == 0:
|
|
101
|
+
if 'List of databases' in line:
|
|
102
|
+
s = 1
|
|
103
|
+
elif s == 1:
|
|
104
|
+
if 'Name' in line and 'Owner' in line and 'Encoding' in line:
|
|
105
|
+
s = 2
|
|
106
|
+
elif s == 2:
|
|
107
|
+
if line.startswith('---------'):
|
|
108
|
+
s = 3
|
|
109
|
+
elif s == 3:
|
|
110
|
+
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
111
|
+
if groups and groups[1] != '|':
|
|
112
|
+
dbs.append({'name': groups[1], 'owner': groups[2]})
|
|
113
113
|
|
|
114
114
|
return dbs
|
|
115
115
|
|
|
@@ -120,23 +120,23 @@ class PostgresSession:
|
|
|
120
120
|
# ----------+------------------------------------------------------------+-------+---------------
|
|
121
121
|
# postgres | c3_2_admin_aclpriv | table | postgres
|
|
122
122
|
# postgres | c3_2_admin_aclpriv_a | table | postgres
|
|
123
|
-
r
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
123
|
+
if r := self.run_sql('\dt', show_out=False):
|
|
124
|
+
s = 0
|
|
125
|
+
for line in r.stdout.split('\n'):
|
|
126
|
+
line: str = line.strip(' \r')
|
|
127
|
+
if s == 0:
|
|
128
|
+
if 'List of relations' in line:
|
|
129
|
+
s = 1
|
|
130
|
+
elif s == 1:
|
|
131
|
+
if 'Schema' in line and 'Name' in line and 'Type' in line:
|
|
132
|
+
s = 2
|
|
133
|
+
elif s == 2:
|
|
134
|
+
if line.startswith('---------'):
|
|
135
|
+
s = 3
|
|
136
|
+
elif s == 3:
|
|
137
|
+
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
138
|
+
if groups and groups[1] != '|':
|
|
139
|
+
dbs.append({'schema': groups[1], 'name': groups[2]})
|
|
140
140
|
|
|
141
141
|
return dbs
|
|
142
142
|
|
|
@@ -156,28 +156,35 @@ class PostgresSession:
|
|
|
156
156
|
return r
|
|
157
157
|
else:
|
|
158
158
|
ns = self.namespace
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
pod_name = Config().get('pg.agent.name', 'ops')
|
|
160
|
+
|
|
161
|
+
if Config().get('pg.agent.just-in-time', False):
|
|
162
|
+
image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
|
|
163
|
+
timeout = Config().get('pg.agent.timeout', 3600)
|
|
164
|
+
try:
|
|
165
|
+
Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
|
|
166
|
+
except Exception as e:
|
|
167
|
+
if e.status == 409:
|
|
168
|
+
if Pods.completed(ns, pod_name):
|
|
169
|
+
try:
|
|
170
|
+
Pods.delete(pod_name, ns)
|
|
171
|
+
Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
|
|
172
|
+
except Exception as e2:
|
|
173
|
+
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
|
|
174
|
+
|
|
175
|
+
return
|
|
176
|
+
else:
|
|
177
|
+
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
|
|
178
|
+
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
Pods.wait_for_running(ns, pod_name)
|
|
162
182
|
|
|
163
183
|
try:
|
|
164
|
-
Pods.
|
|
165
|
-
except
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
try:
|
|
169
|
-
Pods.delete(pod_name, ns)
|
|
170
|
-
Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
|
|
171
|
-
except Exception as e2:
|
|
172
|
-
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
|
|
173
|
-
|
|
174
|
-
return
|
|
175
|
-
else:
|
|
176
|
-
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
|
|
177
|
-
|
|
178
|
-
return
|
|
179
|
-
|
|
180
|
-
Pods.wait_for_running(ns, pod_name)
|
|
184
|
+
Pods.get(ns, pod_name)
|
|
185
|
+
except:
|
|
186
|
+
log2(f"Could not locate {pod_name} pod.")
|
|
187
|
+
return None
|
|
181
188
|
|
|
182
189
|
cmd = f'PGPASSWORD="{self.password()}" psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
|
|
183
190
|
|
adam/commands/user_entry.py
CHANGED
|
@@ -8,7 +8,7 @@ import threading
|
|
|
8
8
|
import traceback
|
|
9
9
|
|
|
10
10
|
from adam import log
|
|
11
|
-
from adam.commands.
|
|
11
|
+
from adam.commands.deploy.code_utils import get_available_port
|
|
12
12
|
from adam.config import Config
|
|
13
13
|
from adam.sso.idp import Idp
|
|
14
14
|
from adam.app_session import AppSession, IdpLogin
|
adam/embedded_params.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
def config():
|
|
2
|
-
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': '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', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'medusa': {'restore-auto-complete': False}, 'nodetool': {'workers': 32, 'samples': 3, 'commands_in_line': 40}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'
|
|
2
|
+
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': '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', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'medusa': {'restore-auto-complete': False}, 'nodetool': {'workers': 32, 'samples': 3, 'commands_in_line': 40}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,CPU,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.13', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'a', 'auto-enter-app': 'c3/c3', 'auto-enter-only-cluster': True}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'debug': {'timings': False, 'exit-on-error': False, 'show-parallelism': False, 'show-out': False}}
|
adam/k8s_utils/pods.py
CHANGED
|
@@ -30,6 +30,13 @@ class Pods:
|
|
|
30
30
|
except Exception as e:
|
|
31
31
|
log2("Exception when calling CoreV1Api->delete_namespaced_pod: %s\n" % e)
|
|
32
32
|
|
|
33
|
+
def delete_with_selector(namespace: str, label_selector: str):
|
|
34
|
+
v1 = client.CoreV1Api()
|
|
35
|
+
|
|
36
|
+
ret = v1.list_namespaced_pod(namespace=namespace, label_selector=label_selector)
|
|
37
|
+
for i in ret.items:
|
|
38
|
+
v1.delete_namespaced_pod(name=i.metadata.name, namespace=namespace)
|
|
39
|
+
|
|
33
40
|
def on_pods(pods: list[str],
|
|
34
41
|
namespace: str,
|
|
35
42
|
body: Callable[[ThreadPoolExecutor, str, str, bool], T],
|
|
@@ -158,12 +165,12 @@ class Pods:
|
|
|
158
165
|
v1 = client.CoreV1Api()
|
|
159
166
|
return v1.read_namespaced_pod(name=pod_name, namespace=namespace)
|
|
160
167
|
|
|
161
|
-
def create_pod_spec(name: str, image: str, image_pull_secret: str, envs: list, volume_name: str, pvc_name:str, mount_path:str, command: list[str]=None, sa_name=None):
|
|
168
|
+
def create_pod_spec(name: str, image: str, image_pull_secret: str, envs: list, container_security_context: client.V1SecurityContext, volume_name: str, pvc_name:str, mount_path:str, command: list[str]=None, sa_name=None):
|
|
162
169
|
volume_mounts = []
|
|
163
170
|
if volume_name and pvc_name and mount_path:
|
|
164
171
|
volume_mounts=[client.V1VolumeMount(mount_path=mount_path, name=volume_name)]
|
|
165
172
|
|
|
166
|
-
container = client.V1Container(name=name, image=image, env=envs, command=command,
|
|
173
|
+
container = client.V1Container(name=name, image=image, env=envs, security_context=container_security_context, command=command,
|
|
167
174
|
volume_mounts=volume_mounts)
|
|
168
175
|
|
|
169
176
|
volumes = []
|
|
@@ -183,9 +190,12 @@ class Pods:
|
|
|
183
190
|
volumes=volumes
|
|
184
191
|
)
|
|
185
192
|
|
|
186
|
-
def create(namespace: str, pod_name: str, image: str,
|
|
193
|
+
def create(namespace: str, pod_name: str, image: str,
|
|
194
|
+
command: list[str] = None,
|
|
187
195
|
secret: str = None,
|
|
188
196
|
env: dict[str, any] = {},
|
|
197
|
+
container_security_context: client.V1SecurityContext = None,
|
|
198
|
+
labels: dict[str, str] = {},
|
|
189
199
|
volume_name: str = None,
|
|
190
200
|
pvc_name: str = None,
|
|
191
201
|
mount_path: str = None,
|
|
@@ -194,9 +204,14 @@ class Pods:
|
|
|
194
204
|
envs = []
|
|
195
205
|
for k, v in env.items():
|
|
196
206
|
envs.append(client.V1EnvVar(name=str(k), value=str(v)))
|
|
197
|
-
pod = Pods.create_pod_spec(pod_name, image, secret, envs, volume_name, pvc_name, mount_path, command=command, sa_name=sa_name)
|
|
198
|
-
return v1.create_namespaced_pod(
|
|
199
|
-
|
|
207
|
+
pod = Pods.create_pod_spec(pod_name, image, secret, envs, container_security_context, volume_name, pvc_name, mount_path, command=command, sa_name=sa_name)
|
|
208
|
+
return v1.create_namespaced_pod(
|
|
209
|
+
namespace=namespace,
|
|
210
|
+
body=client.V1Pod(spec=pod, metadata=client.V1ObjectMeta(
|
|
211
|
+
name=pod_name,
|
|
212
|
+
labels=labels
|
|
213
|
+
))
|
|
214
|
+
)
|
|
200
215
|
|
|
201
216
|
def wait_for_running(namespace: str, pod_name: str):
|
|
202
217
|
msged = False
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from kubernetes import client, config
|
|
2
|
+
|
|
3
|
+
from adam.config import Config
|
|
4
|
+
|
|
5
|
+
# utility collection on service accounts; methods are all static
|
|
6
|
+
class ServiceAccounts:
|
|
7
|
+
def delete(namespace: str, label_selector: str):
|
|
8
|
+
ServiceAccounts.delete_cluster_role_bindings(label_selector)
|
|
9
|
+
ServiceAccounts.delete_role_bindings(namespace, label_selector)
|
|
10
|
+
ServiceAccounts.delete_service_account(namespace, label_selector)
|
|
11
|
+
|
|
12
|
+
def replicate(to_sa: str, namespace: str, from_sa: str, labels: dict[str, str] = {}, add_cluster_roles: list[str] = []):
|
|
13
|
+
ServiceAccounts.create_service_account(to_sa, namespace, labels=labels)
|
|
14
|
+
for b in ServiceAccounts.get_role_bindings(from_sa, namespace):
|
|
15
|
+
n = f'{to_sa}-{b.role_ref.name}'
|
|
16
|
+
ServiceAccounts.create_role_binding(n, namespace, to_sa, b.role_ref.name, labels=labels)
|
|
17
|
+
|
|
18
|
+
for b in ServiceAccounts.get_cluster_role_bindings(from_sa):
|
|
19
|
+
n = f'{to_sa}-{b.role_ref.name}'
|
|
20
|
+
ServiceAccounts.create_cluster_role_binding(n, namespace, to_sa, b.role_ref.name, labels=labels)
|
|
21
|
+
|
|
22
|
+
for cr in add_cluster_roles:
|
|
23
|
+
n = f'{to_sa}-{cr}'
|
|
24
|
+
ServiceAccounts.create_cluster_role_binding(n, namespace, to_sa, cr, labels=labels)
|
|
25
|
+
|
|
26
|
+
def create_service_account(name: str, namespace: str, labels: dict[str, str] = {}):
|
|
27
|
+
config.load_kube_config()
|
|
28
|
+
|
|
29
|
+
v1 = client.CoreV1Api()
|
|
30
|
+
|
|
31
|
+
service_account = client.V1ServiceAccount(
|
|
32
|
+
metadata=client.V1ObjectMeta(
|
|
33
|
+
name=name,
|
|
34
|
+
labels=labels)
|
|
35
|
+
)
|
|
36
|
+
api_response = v1.create_namespaced_service_account(
|
|
37
|
+
namespace=namespace,
|
|
38
|
+
body=service_account
|
|
39
|
+
)
|
|
40
|
+
Config().debug(f"Service Account '{api_response.metadata.name}' created in namespace '{namespace}'.")
|
|
41
|
+
|
|
42
|
+
def delete_service_account(namespace: str, label_selector: str) -> list:
|
|
43
|
+
refs = []
|
|
44
|
+
|
|
45
|
+
v1 = client.CoreV1Api()
|
|
46
|
+
sas = v1.list_namespaced_service_account(namespace=namespace, label_selector=label_selector).items
|
|
47
|
+
for sa in sas:
|
|
48
|
+
Config().debug(f'delete {sa.metadata.name}')
|
|
49
|
+
v1.delete_namespaced_service_account(name=sa.metadata.name, namespace=namespace)
|
|
50
|
+
refs.append(sa)
|
|
51
|
+
|
|
52
|
+
return refs
|
|
53
|
+
|
|
54
|
+
def create_role_binding(name: str, namespace: str, sa_name: str, role_name: str, labels: dict[str, str] = {}):
|
|
55
|
+
api = client.RbacAuthorizationV1Api()
|
|
56
|
+
|
|
57
|
+
metadata = client.V1ObjectMeta(
|
|
58
|
+
name=name,
|
|
59
|
+
namespace=namespace,
|
|
60
|
+
labels=labels
|
|
61
|
+
)
|
|
62
|
+
role_ref = client.V1RoleRef(
|
|
63
|
+
api_group="rbac.authorization.k8s.io",
|
|
64
|
+
kind="Role",
|
|
65
|
+
name=role_name
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
subjects = [
|
|
69
|
+
client.RbacV1Subject(
|
|
70
|
+
kind="ServiceAccount",
|
|
71
|
+
name=sa_name, # Name of the service account
|
|
72
|
+
namespace=namespace # Namespace of the service account
|
|
73
|
+
)
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
role_binding = client.V1RoleBinding(
|
|
77
|
+
api_version="rbac.authorization.k8s.io/v1",
|
|
78
|
+
kind="RoleBinding",
|
|
79
|
+
metadata=metadata,
|
|
80
|
+
role_ref=role_ref,
|
|
81
|
+
subjects=subjects
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
api.create_namespaced_role_binding(namespace=namespace, body=role_binding)
|
|
85
|
+
|
|
86
|
+
def get_role_bindings(service_account_name: str, namespace: str) -> list:
|
|
87
|
+
refs = []
|
|
88
|
+
|
|
89
|
+
rbac_api = client.RbacAuthorizationV1Api()
|
|
90
|
+
role_bindings = rbac_api.list_namespaced_role_binding(namespace=namespace)
|
|
91
|
+
for binding in role_bindings.items:
|
|
92
|
+
if binding.subjects:
|
|
93
|
+
for subject in binding.subjects:
|
|
94
|
+
if subject.kind == "ServiceAccount" and subject.name == service_account_name:
|
|
95
|
+
refs.append(binding)
|
|
96
|
+
|
|
97
|
+
return refs
|
|
98
|
+
|
|
99
|
+
def delete_role_bindings(namespace: str, label_selector: str) -> list:
|
|
100
|
+
refs = []
|
|
101
|
+
|
|
102
|
+
v1_rbac = client.RbacAuthorizationV1Api()
|
|
103
|
+
cluster_role_bindings = v1_rbac.list_namespaced_role_binding(namespace=namespace, label_selector=label_selector).items
|
|
104
|
+
for binding in cluster_role_bindings:
|
|
105
|
+
Config().debug(f'delete {binding.metadata.name}')
|
|
106
|
+
v1_rbac.delete_namespaced_role_binding(name=binding.metadata.name, namespace=namespace)
|
|
107
|
+
refs.append(binding)
|
|
108
|
+
|
|
109
|
+
return refs
|
|
110
|
+
|
|
111
|
+
def create_cluster_role_binding(name: str, namespace: str, sa_name: str, role_name: str, labels: dict[str, str] = {}):
|
|
112
|
+
api = client.RbacAuthorizationV1Api()
|
|
113
|
+
|
|
114
|
+
metadata = client.V1ObjectMeta(
|
|
115
|
+
name=name,
|
|
116
|
+
namespace=namespace,
|
|
117
|
+
labels=labels
|
|
118
|
+
)
|
|
119
|
+
role_ref = client.V1RoleRef(
|
|
120
|
+
api_group="rbac.authorization.k8s.io",
|
|
121
|
+
kind="ClusterRole",
|
|
122
|
+
name=role_name
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
subjects = [
|
|
126
|
+
client.RbacV1Subject(
|
|
127
|
+
kind="ServiceAccount",
|
|
128
|
+
name=sa_name,
|
|
129
|
+
namespace=namespace
|
|
130
|
+
)
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
role_binding = client.V1ClusterRoleBinding(
|
|
134
|
+
api_version="rbac.authorization.k8s.io/v1",
|
|
135
|
+
metadata=metadata,
|
|
136
|
+
role_ref=role_ref,
|
|
137
|
+
subjects=subjects
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
api.create_cluster_role_binding(body=role_binding)
|
|
142
|
+
except client.ApiException as e:
|
|
143
|
+
print(f"Error creating ClusterRoleBinding: {e}")
|
|
144
|
+
|
|
145
|
+
def get_cluster_role_bindings(service_account_name: str) -> list:
|
|
146
|
+
refs = []
|
|
147
|
+
|
|
148
|
+
v1_rbac = client.RbacAuthorizationV1Api()
|
|
149
|
+
cluster_role_bindings = v1_rbac.list_cluster_role_binding().items
|
|
150
|
+
for binding in cluster_role_bindings:
|
|
151
|
+
if binding.subjects:
|
|
152
|
+
for subject in binding.subjects:
|
|
153
|
+
if subject.kind == "ServiceAccount" and subject.name == service_account_name:
|
|
154
|
+
refs.append(binding)
|
|
155
|
+
|
|
156
|
+
return refs
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def delete_cluster_role_bindings(label_selector: str) -> list:
|
|
160
|
+
refs = []
|
|
161
|
+
|
|
162
|
+
v1_rbac = client.RbacAuthorizationV1Api()
|
|
163
|
+
cluster_role_bindings = v1_rbac.list_cluster_role_binding(label_selector=label_selector).items
|
|
164
|
+
for binding in cluster_role_bindings:
|
|
165
|
+
Config().debug(f'delete {binding.metadata.name}')
|
|
166
|
+
v1_rbac.delete_cluster_role_binding(binding.metadata.name)
|
|
167
|
+
refs.append(binding)
|
|
168
|
+
|
|
169
|
+
return refs
|
adam/repl_commands.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from adam.commands.app import App
|
|
2
2
|
from adam.commands.app_ping import AppPing
|
|
3
|
-
from adam.commands.
|
|
4
|
-
from adam.commands.
|
|
5
|
-
from adam.commands.
|
|
6
|
-
from adam.commands.
|
|
7
|
-
from adam.commands.
|
|
8
|
-
from adam.commands.
|
|
3
|
+
from adam.commands.deploy.code_start import CodeStart
|
|
4
|
+
from adam.commands.deploy.code_stop import CodeStop
|
|
5
|
+
from adam.commands.deploy.deploy import Deploy
|
|
6
|
+
from adam.commands.deploy.deploy_frontend import DeployFrontend
|
|
7
|
+
from adam.commands.deploy.deploy_pod import DeployPod
|
|
8
|
+
from adam.commands.deploy.undeploy import Undeploy
|
|
9
|
+
from adam.commands.deploy.undeploy_frontend import UndeployFrontend
|
|
10
|
+
from adam.commands.deploy.undeploy_pod import UndeployPod
|
|
9
11
|
from adam.commands.show.show_app_queues import ShowAppQueues
|
|
10
12
|
from adam.commands.cp import ClipboardCopy
|
|
11
13
|
from adam.commands.bash import Bash
|
|
@@ -48,7 +50,7 @@ class ReplCommands:
|
|
|
48
50
|
cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_check() + ReplCommands.cassandra_ops() + \
|
|
49
51
|
ReplCommands.tools() + ReplCommands.app() + ReplCommands.exit()
|
|
50
52
|
|
|
51
|
-
intermediate_cmds: list[Command] = [App(), Reaper(), Repair(),
|
|
53
|
+
intermediate_cmds: list[Command] = [App(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
|
|
52
54
|
ic = [c.command() for c in intermediate_cmds]
|
|
53
55
|
# 1. dedup commands
|
|
54
56
|
deduped = []
|
|
@@ -75,7 +77,7 @@ class ReplCommands:
|
|
|
75
77
|
return Medusa.cmd_list() + [Restart(), RollOut(), Watch()] + Reaper.cmd_list() + Repair.cmd_list()
|
|
76
78
|
|
|
77
79
|
def tools() -> list[Command]:
|
|
78
|
-
return [Cqlsh(), Postgres(), Bash(), CodeStart(), CodeStop(),
|
|
80
|
+
return [Cqlsh(), Postgres(), Bash(), CodeStart(), CodeStop(), DeployFrontend(), UndeployFrontend(), DeployPod(), UndeployPod()]
|
|
79
81
|
|
|
80
82
|
def app() -> list[Command]:
|
|
81
83
|
return [ShowAppActions(), ShowAppId(), ShowAppQueues(), AppPing(), App()]
|
adam/sso/authenticator.py
CHANGED
|
@@ -9,7 +9,7 @@ class Authenticator:
|
|
|
9
9
|
return None
|
|
10
10
|
|
|
11
11
|
@abstractmethod
|
|
12
|
-
def authenticate(self, idp_uri: str, app_host: str, username: str, password: str) -> IdpLogin:
|
|
12
|
+
def authenticate(self, idp_uri: str, app_host: str, username: str, password: str, verify: bool = True) -> IdpLogin:
|
|
13
13
|
pass
|
|
14
14
|
|
|
15
15
|
def extract(self, form: str, pattern: re.Pattern):
|
adam/sso/authn_ad.py
CHANGED
|
@@ -24,7 +24,7 @@ class AdAuthenticator(Authenticator):
|
|
|
24
24
|
|
|
25
25
|
return cls.instance
|
|
26
26
|
|
|
27
|
-
def authenticate(self, idp_uri: str, app_host: str, username: str, password: str) -> IdpLogin:
|
|
27
|
+
def authenticate(self, idp_uri: str, app_host: str, username: str, password: str, verify: bool) -> IdpLogin:
|
|
28
28
|
parsed_url = urlparse(idp_uri)
|
|
29
29
|
query_string = parsed_url.query
|
|
30
30
|
params = parse_qs(query_string)
|
|
@@ -84,6 +84,9 @@ class AdAuthenticator(Authenticator):
|
|
|
84
84
|
if not id_token:
|
|
85
85
|
raise AdException('Invalid username/password.')
|
|
86
86
|
|
|
87
|
+
if not verify:
|
|
88
|
+
return IdpLogin(redirect_url, id_token, state_token, username, idp_uri=idp_uri, id_token_obj=None, session=session)
|
|
89
|
+
|
|
87
90
|
parsed = self.parse_id_token(id_token)
|
|
88
91
|
roles = parsed.groups
|
|
89
92
|
roles.append(username)
|
adam/sso/authn_okta.py
CHANGED
|
@@ -23,7 +23,7 @@ class OktaAuthenticator(Authenticator):
|
|
|
23
23
|
|
|
24
24
|
return cls.instance
|
|
25
25
|
|
|
26
|
-
def authenticate(self, idp_uri: str, app_host: str, username: str, password: str) -> IdpLogin:
|
|
26
|
+
def authenticate(self, idp_uri: str, app_host: str, username: str, password: str, verify: bool) -> IdpLogin:
|
|
27
27
|
parsed_url = urlparse(idp_uri)
|
|
28
28
|
query_string = parsed_url.query
|
|
29
29
|
params = parse_qs(query_string)
|
|
@@ -71,6 +71,10 @@ class OktaAuthenticator(Authenticator):
|
|
|
71
71
|
|
|
72
72
|
raise OktaException('Invalid username/password.')
|
|
73
73
|
|
|
74
|
+
if not verify:
|
|
75
|
+
# just relay id_token, it will be verified by SP
|
|
76
|
+
return IdpLogin(redirect_url, id_token, state_token, username, idp_uri=idp_uri, id_token_obj=None, session=session)
|
|
77
|
+
|
|
74
78
|
if group := Config().get('app.login.admin-group', '{host}/C3.ClusterAdmin').replace('{host}', app_host):
|
|
75
79
|
parsed = OktaAuthenticator.parse_id_token(okta_host, id_token)
|
|
76
80
|
if group not in parsed.groups:
|
adam/sso/idp.py
CHANGED
|
@@ -28,7 +28,7 @@ class Idp:
|
|
|
28
28
|
|
|
29
29
|
return cls.instance
|
|
30
30
|
|
|
31
|
-
def login(app_host: str, username: str = None, idp_uri: str = None, forced = False, use_token_from_env = True, use_cached_creds = True) -> IdpLogin:
|
|
31
|
+
def login(app_host: str, username: str = None, idp_uri: str = None, forced = False, use_token_from_env = True, use_cached_creds = True, verify = True) -> IdpLogin:
|
|
32
32
|
session: IdpSession = IdpSession.create(username, app_host, app_host, idp_uri=idp_uri)
|
|
33
33
|
|
|
34
34
|
if use_token_from_env:
|
|
@@ -95,13 +95,13 @@ class Idp:
|
|
|
95
95
|
|
|
96
96
|
if username and password:
|
|
97
97
|
# if uploading kubeconfig file fails many times, you will be locked out
|
|
98
|
-
# kubeconfig file content has first char as tab
|
|
98
|
+
# kubeconfig file content has first char as tab or length of bigger than 128
|
|
99
99
|
if password[0] == '\t' or len(password) > Config().get('app.login.password-max-length', 128):
|
|
100
100
|
if r := Idp.try_kubeconfig(username, password):
|
|
101
101
|
log(f"You're signed in as {username}")
|
|
102
102
|
return r
|
|
103
103
|
else:
|
|
104
|
-
if r := session.authenticator.authenticate(session.idp_uri, app_host, username, password):
|
|
104
|
+
if r := session.authenticator.authenticate(session.idp_uri, app_host, username, password, verify=verify):
|
|
105
105
|
log(f"You're signed in as {username}")
|
|
106
106
|
return r
|
|
107
107
|
finally:
|
adam/version.py
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
adam/__init__.py,sha256=oVw1FNd9HZPJ7wm6BNn5ybyNGJLjJ8kopMeBiwgMaOI,59
|
|
2
|
-
adam/app_session.py,sha256=
|
|
2
|
+
adam/app_session.py,sha256=Klypm4JYHOlovaRCHAZ2P_Mj_nheMlcQgX403R0TJGk,6969
|
|
3
3
|
adam/apps.py,sha256=UTpUJBAMRFvR8kJZwileGC0UmPvsOjJ_AgvWoGmnIFI,6701
|
|
4
|
-
adam/batch.py,sha256=
|
|
4
|
+
adam/batch.py,sha256=vJRniGz0L8CXluVqP_EMFnthuZdILBMNUmNzxz9ImeI,24110
|
|
5
5
|
adam/cli.py,sha256=03pIZdomAu7IL-GSP6Eun_PKwwISShRAmfx6eVRPGC0,458
|
|
6
6
|
adam/cli_group.py,sha256=W3zy1BghCtVcEXizq8fBH-93ZRVVwgAyGPzy0sHno1Y,593
|
|
7
7
|
adam/config.py,sha256=38UcmYRxf-Kq4iPbKS7tNPQqN64fam1bWNy6jhWREd0,2552
|
|
8
8
|
adam/embedded_apps.py,sha256=lKPx63mKzJbNmwz0rgL4gF76M9fDGxraYTtNAIGnZ_s,419
|
|
9
|
-
adam/embedded_params.py,sha256=
|
|
9
|
+
adam/embedded_params.py,sha256=sHOxnZLjaDX7oN5VKeCzCpbEMb-1x6Nn9lRA7PE7mos,4362
|
|
10
10
|
adam/log.py,sha256=gg5DK52wLPc9cjykeh0WFHyAk1qI3HEpGaAK8W2dzXY,1146
|
|
11
11
|
adam/pod_exec_result.py,sha256=nq0xnCNOpUGBSijGF0H-YNrwBc9vUQs4DkvLMIFS5LQ,951
|
|
12
12
|
adam/repl.py,sha256=wEzkXaFaT1PWWYI3AZ32j01efN7HpL2xvMfGLEmYIL4,7036
|
|
13
|
-
adam/repl_commands.py,sha256=
|
|
13
|
+
adam/repl_commands.py,sha256=iHR9LqUzBveRVuuPhLdF8M9CayboFCzYNmz6xP0byME,4046
|
|
14
14
|
adam/repl_session.py,sha256=uIogcvWBh7wd8QQ-p_JgLsyJ8YJgINw5vOd6JIsd7Vo,472
|
|
15
15
|
adam/repl_state.py,sha256=QarrUAwYWOz3YTemtaf2opbHLa5a3LEsyuonNwhvOhk,7131
|
|
16
16
|
adam/utils.py,sha256=j7p7iruLuV11swa0z9ZLBgoJHu_nkTSVKtQe0q71gmk,7025
|
|
17
|
-
adam/version.py,sha256=
|
|
17
|
+
adam/version.py,sha256=b_uKoBDt32TpOid0_wR1sYA_Ut__Wbgm53S3X58HGdE,140
|
|
18
18
|
adam/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
adam/checks/check.py,sha256=Qopr3huYcMu2bzQgb99dEUYjFzkjKHRI76S6KA9b9Rk,702
|
|
20
20
|
adam/checks/check_context.py,sha256=FEHkQ32jY1EDopQ2uYWqy9v7aEEX1orLpJWhopwAlh4,402
|
|
@@ -75,16 +75,19 @@ adam/commands/pwd.py,sha256=VlgFjxFl66I7Df1YI6cuiEeY6Q13lEavMKfrzHLESKo,2340
|
|
|
75
75
|
adam/commands/report.py,sha256=Ky45LIzSlB_X4V12JZWjU3SA2u4_FKRencRTq7psOWU,1944
|
|
76
76
|
adam/commands/restart.py,sha256=Hik1t5rjH2ATZv4tzwrGB3e44b3dNuATgY327_Nb8Bs,2044
|
|
77
77
|
adam/commands/rollout.py,sha256=52_4ijna3v-8Oug12et43DRHFDNhiN34p6xLTQmhdbQ,2959
|
|
78
|
-
adam/commands/user_entry.py,sha256=
|
|
78
|
+
adam/commands/user_entry.py,sha256=7PeRYPpGU4x3kyQnUBxVaOH47anx7YwVPSIz-0Rfpko,4740
|
|
79
79
|
adam/commands/watch.py,sha256=mmBFpB8T1V7zrNs5b2YNyDDztMym_ILPDdkrbdAXTas,2438
|
|
80
|
-
adam/commands/
|
|
81
|
-
adam/commands/
|
|
82
|
-
adam/commands/
|
|
83
|
-
adam/commands/
|
|
84
|
-
adam/commands/
|
|
85
|
-
adam/commands/
|
|
86
|
-
adam/commands/
|
|
87
|
-
adam/commands/
|
|
80
|
+
adam/commands/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
81
|
+
adam/commands/deploy/code_start.py,sha256=-iH8HThTNM83IfBxT_LqTByuHVatV9d-Il4OYOfrwLI,1370
|
|
82
|
+
adam/commands/deploy/code_stop.py,sha256=ch7ZMgosvTHsGaIcDwQY5XYh_5HYrUjBkZFOI-d2gOU,1696
|
|
83
|
+
adam/commands/deploy/code_utils.py,sha256=5Gp4U8HzKpPkbkHDU7whlvGOK-wWaAbCIIGzVoN9hio,3296
|
|
84
|
+
adam/commands/deploy/deploy.py,sha256=ymGprq2rBpAMsxaqRrVnrcYetAq9GDTguohlAWFuFa8,1855
|
|
85
|
+
adam/commands/deploy/deploy_frontend.py,sha256=kc4GnpRMa3a384tLN8d9FN1XUcRrbwkpVUY1O3YkJU0,1774
|
|
86
|
+
adam/commands/deploy/deploy_pod.py,sha256=cJCt4DlNrr_JuDb_X3h_8b2Zsh8k7uzu2F5FeFmAirQ,2522
|
|
87
|
+
adam/commands/deploy/deploy_utils.py,sha256=7KZAG7oz9U0EtV2Q6kw8q4x89RmeaTxQjnLfH5KZC5Q,1166
|
|
88
|
+
adam/commands/deploy/undeploy.py,sha256=JWRwYLiS7iD2utRr2Zb8HCg_rbgHTw-A17CCs2ezl00,1949
|
|
89
|
+
adam/commands/deploy/undeploy_frontend.py,sha256=P04l_DI6y1dAqHMQMG94MG7K4piCw84zhPszc4Lz28o,1349
|
|
90
|
+
adam/commands/deploy/undeploy_pod.py,sha256=xiqenETzyzvQK0fH6OPtfNpww9E8ArHHFMWp9rTC6aU,1842
|
|
88
91
|
adam/commands/medusa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
92
|
adam/commands/medusa/medusa.py,sha256=Y_yyOZRb6u45wfTVBRp3kuklyYDTSlaAJQdAiymP_8M,2185
|
|
90
93
|
adam/commands/medusa/medusa_backup.py,sha256=j4DTVWFT-4rzs4gG_pBvjE-JuPsVCJIsnyQjIzJ4EbA,1801
|
|
@@ -95,7 +98,7 @@ adam/commands/postgres/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
95
98
|
adam/commands/postgres/postgres.py,sha256=ugnQulU6h3pSySYRwU801raxqsC8W5DcFLSbqnk7SPo,3597
|
|
96
99
|
adam/commands/postgres/postgres_ls.py,sha256=HwZTgwGKXUqHX33S8aQPF6FqCrLqtoz4cLyJV2SpoE0,1186
|
|
97
100
|
adam/commands/postgres/postgres_preview.py,sha256=OhnWe6SKf2Z8rBW7_NJEvJFlLqPUKMr528VPVtoT0yw,1306
|
|
98
|
-
adam/commands/postgres/postgres_session.py,sha256=
|
|
101
|
+
adam/commands/postgres/postgres_session.py,sha256=u9KWgIWt1PknlpPMPtk6s8dwi0rgrHAuozvuDFUtoyE,8933
|
|
99
102
|
adam/commands/reaper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
103
|
adam/commands/reaper/reaper.py,sha256=SbxXgJ6b4wdXx5a1Fps71iEa1hmycWbArh1oC5bS83M,2677
|
|
101
104
|
adam/commands/reaper/reaper_forward.py,sha256=mUp409MzT91cVXGxoPfBGceaR3qZ0rVdWKGdyzPNzSA,3177
|
|
@@ -137,23 +140,24 @@ adam/k8s_utils/custom_resources.py,sha256=cIeaZRQET2DelTGU2f5QsMckh7TddPpWZDFeNK
|
|
|
137
140
|
adam/k8s_utils/ingresses.py,sha256=ul3Z6fDGc_Cxcn-ExP0vXhZatoShCUZFtpwtCY4Qx7o,3460
|
|
138
141
|
adam/k8s_utils/jobs.py,sha256=P7j3JiZ33TRnkjkPLLrGlypAPxK1BZQHvVpF8s7eHA8,2604
|
|
139
142
|
adam/k8s_utils/kube_context.py,sha256=nocEyVNjXWG-N-GNnEYHDvnot7H5LQUFmpZIwIyE7As,3310
|
|
140
|
-
adam/k8s_utils/pods.py,sha256=
|
|
143
|
+
adam/k8s_utils/pods.py,sha256=OnQo59k01lyZ_ZKBW2--8itZ5xgKoU17p5SFSm1le6o,8608
|
|
141
144
|
adam/k8s_utils/secrets.py,sha256=pYaVKXTpx3-QgFtBjznWFq0N6ZcBdxnx21FRe5nBCCo,2305
|
|
145
|
+
adam/k8s_utils/service_accounts.py,sha256=v2oQSqCrNvt2uRnKlNwR3fjtpUG7oF5nqgzEB7NnT-U,6349
|
|
142
146
|
adam/k8s_utils/services.py,sha256=EOJJGACVbbRvu5T3rMKqIJqgYic1_MSJ17EA0TJ6UOk,3156
|
|
143
147
|
adam/k8s_utils/statefulsets.py,sha256=PZDEhy34aXxLkbW1-RsOC0E4D0w0pHyoIQGHvcAzSAk,4606
|
|
144
148
|
adam/k8s_utils/volumes.py,sha256=MzYeH80NqKlhdadx6d0tW-j8vTOCUYWx7wRURIZWKZ8,843
|
|
145
149
|
adam/sso/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
146
|
-
adam/sso/authenticator.py,sha256=
|
|
147
|
-
adam/sso/authn_ad.py,sha256=
|
|
148
|
-
adam/sso/authn_okta.py,sha256=
|
|
150
|
+
adam/sso/authenticator.py,sha256=BCm16L9zf5aLU47-sTCnudn2zLPwd8M2wwRminJfsqw,615
|
|
151
|
+
adam/sso/authn_ad.py,sha256=BfKoltUG3jP0vNe-MImhnjkufBo79aH1HGZcQtHFH_Q,5912
|
|
152
|
+
adam/sso/authn_okta.py,sha256=TV5vg7OEQPGFG_DSUQnWn37nbMX_qszZB0GRuQl6kGM,4529
|
|
149
153
|
adam/sso/cred_cache.py,sha256=7WA5rIy1wlr_GCF-Z6xRb6LzRu-Cvou-IkY7hWC3Zpc,2099
|
|
150
154
|
adam/sso/id_token.py,sha256=wmVZ8S0sjScnOxmSvOKlIEKgnvdWqhsgq9XjFe355O4,744
|
|
151
|
-
adam/sso/idp.py,sha256=
|
|
155
|
+
adam/sso/idp.py,sha256=fvcwUw_URTgsO6ySaqTIw0zQT2qRO1IPSGhf6rPtybo,5804
|
|
152
156
|
adam/sso/idp_login.py,sha256=t49CRlMyHA76BAj_kKq0Wa9URIYlzBsUCSmn7Jf5o6I,1721
|
|
153
157
|
adam/sso/idp_session.py,sha256=9BUHNRf70u4rVKrVY1HKPOEmOviXvkjam8WJxmXSKIM,1735
|
|
154
158
|
adam/sso/sso_config.py,sha256=5N8WZgIJQBtHUy585XLRWKjpU87_v6QluyNK9E27D5s,2459
|
|
155
|
-
kaqing-1.98.
|
|
156
|
-
kaqing-1.98.
|
|
157
|
-
kaqing-1.98.
|
|
158
|
-
kaqing-1.98.
|
|
159
|
-
kaqing-1.98.
|
|
159
|
+
kaqing-1.98.88.dist-info/METADATA,sha256=LquFVHPcx1N3O_uIWngJKW2sLmWKBfE0vhEI1X5nwZA,133
|
|
160
|
+
kaqing-1.98.88.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
161
|
+
kaqing-1.98.88.dist-info/entry_points.txt,sha256=SkzhuQJUWsXOzHeZ5TgQ2c3_g53UGK23zzJU_JTZOZI,39
|
|
162
|
+
kaqing-1.98.88.dist-info/top_level.txt,sha256=8_2PZkwBb-xDcnc8a2rAbQeJhXKXskc7zTP7pSPa1fw,5
|
|
163
|
+
kaqing-1.98.88.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|