pyntcli 0.1.73__py3-none-any.whl → 0.1.74__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.
- pyntcli/__init__.py +1 -1
- pyntcli/analytics/send.py +32 -17
- pyntcli/auth/login.py +41 -31
- pyntcli/commands/burp.py +8 -16
- pyntcli/commands/command.py +15 -30
- pyntcli/commands/id_command.py +5 -3
- pyntcli/commands/listen.py +20 -45
- pyntcli/commands/newman.py +2 -2
- pyntcli/commands/postman.py +26 -25
- pyntcli/commands/pynt_cmd.py +27 -13
- pyntcli/commands/root.py +2 -0
- pyntcli/log/log.py +2 -1
- pyntcli/main.py +4 -3
- pyntcli/pynt_docker/pynt_container.py +65 -53
- pyntcli/store/store.py +19 -14
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.74.dist-info}/METADATA +1 -1
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.74.dist-info}/RECORD +21 -21
- tests/auth/test_login.py +17 -19
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.74.dist-info}/WHEEL +0 -0
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.74.dist-info}/entry_points.txt +0 -0
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.74.dist-info}/top_level.txt +0 -0
pyntcli/commands/postman.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import time
|
|
2
|
+
import time
|
|
3
3
|
import websocket
|
|
4
4
|
import webbrowser
|
|
5
5
|
import os
|
|
@@ -14,28 +14,31 @@ from pyntcli.pynt_docker import pynt_container
|
|
|
14
14
|
from pyntcli.ui import ui_thread
|
|
15
15
|
from pyntcli.transport import pynt_requests
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
class PyntPostmanException(Exception):
|
|
18
19
|
pass
|
|
19
20
|
|
|
21
|
+
|
|
20
22
|
class PyntWebSocketException(PyntPostmanException):
|
|
21
23
|
pass
|
|
22
24
|
|
|
25
|
+
|
|
23
26
|
logger = log.get_logger()
|
|
24
27
|
|
|
28
|
+
|
|
25
29
|
def postman_usage():
|
|
26
30
|
return ui_thread.PrinterText("Integration with postman, run scan from pynt postman collection") \
|
|
27
31
|
.with_line("") \
|
|
28
|
-
.with_line("Usage:",style=ui_thread.PrinterText.HEADER) \
|
|
32
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER) \
|
|
29
33
|
.with_line("\tpynt postman [OPTIONS]") \
|
|
30
34
|
.with_line("") \
|
|
31
|
-
.with_line("Options:",style=ui_thread.PrinterText.HEADER) \
|
|
35
|
+
.with_line("Options:", style=ui_thread.PrinterText.HEADER) \
|
|
32
36
|
.with_line("\t--port - set the port pynt will listen to (DEFAULT: 5001)") \
|
|
33
37
|
.with_line("\t--insecure - use when target uses self signed certificates") \
|
|
34
38
|
.with_line("\t--host-ca - path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN.")
|
|
35
39
|
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
class PostmanSubCommand(sub_command.PyntSubCommand):
|
|
41
|
+
class PostmanSubCommand(sub_command.PyntSubCommand):
|
|
39
42
|
def __init__(self, name) -> None:
|
|
40
43
|
super().__init__(name)
|
|
41
44
|
self.server_base_url = "http://localhost:{}/api"
|
|
@@ -43,18 +46,17 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
|
|
|
43
46
|
def usage(self, *args):
|
|
44
47
|
ui_thread.print(postman_usage())
|
|
45
48
|
|
|
46
|
-
def add_cmd(self, parent_command: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
49
|
+
def add_cmd(self, parent_command: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
47
50
|
postman_cmd = parent_command.add_parser(self.name)
|
|
48
51
|
postman_cmd.add_argument("--port", "-p", help="set the port pynt will listen to (DEFAULT: 5001)", type=int, default=5001)
|
|
49
52
|
postman_cmd.print_usage = self.usage
|
|
50
53
|
postman_cmd.print_help = self.usage
|
|
51
54
|
return postman_cmd
|
|
52
|
-
|
|
53
55
|
|
|
54
56
|
def scan_id_generator(self, port):
|
|
55
57
|
try:
|
|
56
58
|
ws = websocket.WebSocket()
|
|
57
|
-
ws.connect("ws://localhost:{}/api/scan_id".format(port))
|
|
59
|
+
ws.connect("ws://localhost:{}/api/scan_id".format(port))
|
|
58
60
|
|
|
59
61
|
while ws.connected:
|
|
60
62
|
scan_id = ws.recv()
|
|
@@ -69,7 +71,7 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
|
|
|
69
71
|
finally:
|
|
70
72
|
ws.close()
|
|
71
73
|
|
|
72
|
-
def get_report(self, port,report_format, scan_id):
|
|
74
|
+
def get_report(self, port, report_format, scan_id):
|
|
73
75
|
while True:
|
|
74
76
|
res = pynt_requests.get(self.server_base_url.format(port) + "/report?format={}".format(report_format), params={"scanId": scan_id})
|
|
75
77
|
if res.status_code == HTTPStatus.OK:
|
|
@@ -79,42 +81,41 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
|
|
|
79
81
|
continue
|
|
80
82
|
if res.status_code == HTTPStatus.BAD_REQUEST:
|
|
81
83
|
return
|
|
82
|
-
if res.status_code == 517:
|
|
84
|
+
if res.status_code == 517: # pynt did not recieve any requests
|
|
83
85
|
ui_thread.print(ui_thread.PrinterText(res.json()["message"], ui_thread.PrinterText.WARNING))
|
|
84
|
-
return
|
|
86
|
+
return
|
|
85
87
|
ui_thread.print("Error in polling for scan report: {}".format(res.text))
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
+
return
|
|
88
89
|
|
|
89
90
|
def run_cmd(self, args: argparse.Namespace):
|
|
90
|
-
if "application_id" in args and args.application_id:
|
|
91
|
-
ui_thread.print("application-id is not supported in postman integration, use the
|
|
91
|
+
if "application_id" in args and args.application_id:
|
|
92
|
+
ui_thread.print("application-id is not supported in postman integration, use the collection variables to set application id.")
|
|
92
93
|
args.application_id = ""
|
|
93
94
|
|
|
94
|
-
container = pynt_container.get_container_with_arguments(args
|
|
95
|
-
|
|
95
|
+
container = pynt_container.get_container_with_arguments(args, pynt_container.PyntDockerPort("5001", args.port, name="--port"))
|
|
96
|
+
|
|
96
97
|
if util.is_port_in_use(args.port):
|
|
97
98
|
ui_thread.print(ui_thread.PrinterText("Port: {} already in use, please use a different one".format(args.port), ui_thread.PrinterText.WARNING))
|
|
98
99
|
return
|
|
99
100
|
|
|
100
|
-
postman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
postman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
102
|
+
tag="postman-latest",
|
|
103
|
+
detach=True,
|
|
104
|
+
base_container=container)
|
|
104
105
|
|
|
105
106
|
postman_docker.run()
|
|
106
|
-
ui_thread.print_generator(postman_docker.stdout)
|
|
107
|
-
|
|
107
|
+
ui_thread.print_generator(postman_docker.stdout)
|
|
108
|
+
|
|
108
109
|
util.wait_for_healthcheck("http://localhost:{}".format(args.port))
|
|
109
110
|
|
|
110
111
|
for scan_id in self.scan_id_generator(args.port):
|
|
111
|
-
html_report = self.get_report(args.port, "html",scan_id)
|
|
112
|
+
html_report = self.get_report(args.port, "html", scan_id)
|
|
112
113
|
html_report_path = os.path.join(tempfile.gettempdir(), "pynt_report_{}.html".format(int(time.time())))
|
|
113
114
|
|
|
114
115
|
if html_report:
|
|
115
116
|
with open(html_report_path, "w", encoding="utf-8") as html_file:
|
|
116
117
|
html_file.write(html_report)
|
|
117
118
|
webbrowser.open("file://{}".format(html_report_path))
|
|
118
|
-
|
|
119
|
+
|
|
119
120
|
if not postman_docker.is_alive():
|
|
120
121
|
ui_thread.print(ui_thread.PrinterText("Pynt container is not available", ui_thread.PrinterText.WARNING))
|
pyntcli/commands/pynt_cmd.py
CHANGED
|
@@ -5,6 +5,9 @@ from pyntcli.analytics import send as analytics
|
|
|
5
5
|
from pyntcli.transport import pynt_requests
|
|
6
6
|
from pyntcli.ui import ui_thread
|
|
7
7
|
|
|
8
|
+
from requests.exceptions import SSLError, HTTPError
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
from . import command, listen, postman, root, sub_command, id_command, newman, har, burp
|
|
9
12
|
|
|
10
13
|
avail_sub_commands = [
|
|
@@ -17,29 +20,40 @@ avail_sub_commands = [
|
|
|
17
20
|
burp.BurpCommand("burp"),
|
|
18
21
|
]
|
|
19
22
|
|
|
23
|
+
|
|
20
24
|
class PyntCommandException(Exception):
|
|
21
25
|
pass
|
|
22
26
|
|
|
27
|
+
|
|
23
28
|
class BadArgumentsException(PyntCommandException):
|
|
24
29
|
pass
|
|
25
30
|
|
|
31
|
+
|
|
26
32
|
class NoSuchCommandException(PyntCommandException):
|
|
27
33
|
pass
|
|
28
34
|
|
|
29
35
|
|
|
30
36
|
VERSION_CHECK_URL = "https://d1efigcr4c19qn.cloudfront.net/cli/version"
|
|
31
37
|
|
|
38
|
+
|
|
32
39
|
def check_is_latest_version(current_version):
|
|
33
|
-
res = pynt_requests.get(VERSION_CHECK_URL)
|
|
34
|
-
res.raise_for_status()
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
try:
|
|
42
|
+
res = pynt_requests.get(VERSION_CHECK_URL)
|
|
43
|
+
res.raise_for_status()
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
ui_thread.print(ui_thread.PrinterText("""Pynt CLI new version is available, upgrade now with:
|
|
40
|
-
python3 -m pip install --upgrade pyntcli""",ui_thread.PrinterText.WARNING))
|
|
45
|
+
latest_versions = res.text.replace("\n", "")
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
if current_version != latest_versions:
|
|
48
|
+
ui_thread.print(ui_thread.PrinterText("""Pynt CLI new version is available, upgrade now with:
|
|
49
|
+
python3 -m pip install --upgrade pyntcli""", ui_thread.PrinterText.WARNING))
|
|
50
|
+
except SSLError:
|
|
51
|
+
ui_thread.print(ui_thread.PrinterText("""Error: Unable to check if Pynt CLI version is up-to-date due to VPN/proxy. Run Pynt with --insecure to fix.""", ui_thread.PrinterText.WARNING))
|
|
52
|
+
except HTTPError:
|
|
53
|
+
ui_thread.print("""Unable to check if Pynt CLI version is up-to-date""")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class PyntCommand:
|
|
43
57
|
def __init__(self) -> None:
|
|
44
58
|
self.base: root.BaseCommand = root.BaseCommand()
|
|
45
59
|
self.sub_commands: Dict[str, sub_command.PyntSubCommand] = {sc.get_name(): sc for sc in avail_sub_commands}
|
|
@@ -50,25 +64,25 @@ class PyntCommand:
|
|
|
50
64
|
for sc in self.sub_commands.values():
|
|
51
65
|
self.base.add_base_arguments(sc.add_cmd(self.base.get_subparser()))
|
|
52
66
|
|
|
53
|
-
def parse_args(self, args_from_cmd: List[str]):
|
|
67
|
+
def parse_args(self, args_from_cmd: List[str]):
|
|
54
68
|
return self.base.cmd().parse_args(args_from_cmd)
|
|
55
69
|
|
|
56
70
|
def run_cmd(self, args: argparse.Namespace):
|
|
57
71
|
if not "command" in args:
|
|
58
72
|
raise BadArgumentsException()
|
|
59
|
-
|
|
60
|
-
command = getattr(args, "command")
|
|
73
|
+
|
|
74
|
+
command = getattr(args, "command")
|
|
61
75
|
if not command in self.sub_commands:
|
|
62
76
|
raise NoSuchCommandException()
|
|
63
|
-
|
|
77
|
+
|
|
64
78
|
if "host_ca" in args and args.host_ca:
|
|
65
79
|
pynt_requests.add_host_ca(args.host_ca)
|
|
66
|
-
|
|
80
|
+
|
|
67
81
|
if "insecure" in args and args.insecure:
|
|
68
82
|
pynt_requests.disable_tls_termination()
|
|
69
83
|
|
|
70
84
|
check_is_latest_version(cli_version)
|
|
71
85
|
analytics.emit(analytics.CLI_START)
|
|
72
|
-
|
|
86
|
+
|
|
73
87
|
self.base.run_cmd(args)
|
|
74
88
|
self.sub_commands[command].run_cmd(args)
|
pyntcli/commands/root.py
CHANGED
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
from os import environ
|
|
5
5
|
|
|
6
6
|
from pyntcli.auth import login
|
|
7
|
+
import pyntcli.log.log as log
|
|
7
8
|
from pyntcli.ui import ui_thread
|
|
8
9
|
from pyntcli.analytics import send as analytics
|
|
9
10
|
|
|
@@ -94,5 +95,6 @@ class BaseCommand:
|
|
|
94
95
|
user_id = login.user_id()
|
|
95
96
|
if user_id:
|
|
96
97
|
analytics.set_user_id(user_id)
|
|
98
|
+
log.add_user_details(user_id)
|
|
97
99
|
|
|
98
100
|
check_cicd_context()
|
pyntcli/log/log.py
CHANGED
pyntcli/main.py
CHANGED
|
@@ -45,6 +45,7 @@ def start_analytics():
|
|
|
45
45
|
user_id = login.user_id()
|
|
46
46
|
if user_id:
|
|
47
47
|
analytics.set_user_id(user_id)
|
|
48
|
+
log.add_user_details(user_id)
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
def main():
|
|
@@ -63,7 +64,7 @@ def main():
|
|
|
63
64
|
ui_thread.print(ui_thread.PrinterText("Docker was unavailable, please make sure docker is installed and running.", ui_thread.PrinterText.WARNING))
|
|
64
65
|
analytics.emit(analytics.ERROR, {"error": "docker unavailable"})
|
|
65
66
|
except SSLError:
|
|
66
|
-
ui_thread.print(ui_thread.PrinterText("We encountered SSL issues and could not proceed, this may be the cause of a VPN or a Firewall in place.", ui_thread.PrinterText.WARNING))
|
|
67
|
+
ui_thread.print(ui_thread.PrinterText("We encountered SSL issues and could not proceed, this may be the cause of a VPN or a Firewall in place. Run again with --insecure", ui_thread.PrinterText.WARNING))
|
|
67
68
|
except login.Timeout:
|
|
68
69
|
ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to incomplete registration, please try again.", ui_thread.PrinterText.WARNING))
|
|
69
70
|
analytics.emit(analytics.ERROR, {"error": "login timeout"})
|
|
@@ -71,8 +72,8 @@ def main():
|
|
|
71
72
|
ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to malformed credentials provided in env vars.", ui_thread.PrinterText.WARNING))
|
|
72
73
|
analytics.emit(analytics.ERROR, {"error": "invalid pynt cli credentials in env vars"})
|
|
73
74
|
except pynt_container.ImageUnavailableException:
|
|
74
|
-
analytics.emit(analytics.ERROR, {"error": "
|
|
75
|
-
|
|
75
|
+
analytics.emit(analytics.ERROR, {"error": "Couldn't pull pynt image and no local image found"})
|
|
76
|
+
ui_thread.print(ui_thread.PrinterText("Error: Couldn't pull pynt image and no local image found.",ui_thread.PrinterText.WARNING))
|
|
76
77
|
except HtmlReportNotCreatedException:
|
|
77
78
|
analytics.emit(analytics.ERROR, {"error": "Html report was not created"})
|
|
78
79
|
pynt_errors.unexpected_error()
|
|
@@ -9,19 +9,23 @@ import base64
|
|
|
9
9
|
from pyntcli.ui import ui_thread
|
|
10
10
|
from pyntcli.analytics import send as analytics
|
|
11
11
|
from pyntcli.store import CredStore
|
|
12
|
-
from pyntcli.auth.login import
|
|
12
|
+
from pyntcli.auth.login import PYNT_ID, PYNT_SAAS, PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2
|
|
13
13
|
|
|
14
14
|
PYNT_DOCKER_IMAGE = "ghcr.io/pynt-io/pynt"
|
|
15
15
|
|
|
16
|
+
|
|
16
17
|
def create_mount(src, destination, mount_type="bind"):
|
|
17
18
|
return Mount(target=destination, source=src, type=mount_type)
|
|
18
19
|
|
|
20
|
+
|
|
19
21
|
class DockerNotAvailableException(Exception):
|
|
20
22
|
pass
|
|
21
23
|
|
|
24
|
+
|
|
22
25
|
class ImageUnavailableException(Exception):
|
|
23
26
|
pass
|
|
24
27
|
|
|
28
|
+
|
|
25
29
|
def get_docker_type():
|
|
26
30
|
try:
|
|
27
31
|
c = docker.from_env()
|
|
@@ -30,29 +34,31 @@ def get_docker_type():
|
|
|
30
34
|
analytics.deferred_emit(analytics.DOCKER_PLATFORM, platform)
|
|
31
35
|
if platform and platform.get("Name"):
|
|
32
36
|
return platform.get("Name")
|
|
33
|
-
|
|
37
|
+
|
|
34
38
|
return ""
|
|
35
39
|
|
|
36
40
|
except DockerException:
|
|
37
41
|
raise DockerNotAvailableException()
|
|
38
|
-
except Exception:
|
|
42
|
+
except Exception: # TODO: This is since windows is not behaving nice
|
|
39
43
|
raise DockerNotAvailableException()
|
|
40
44
|
|
|
41
45
|
|
|
42
46
|
class PyntBaseConatiner():
|
|
43
|
-
def __init__(self,docker_type,docker_arguments,mounts, environment={}) -> None:
|
|
47
|
+
def __init__(self, docker_type, docker_arguments, mounts, environment={}) -> None:
|
|
44
48
|
self.docker_type = docker_type
|
|
45
49
|
self.docker_arguments = docker_arguments
|
|
46
50
|
self.mounts = mounts
|
|
47
51
|
self.environment = environment
|
|
48
52
|
|
|
53
|
+
|
|
49
54
|
class PyntDockerPort:
|
|
50
55
|
def __init__(self, src, dest, name) -> None:
|
|
51
56
|
self.src = src
|
|
52
57
|
self.dest = dest
|
|
53
58
|
self.name = name
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
|
|
61
|
+
def get_container_with_arguments(args: argparse.Namespace, *port_args: PyntDockerPort) -> PyntBaseConatiner:
|
|
56
62
|
docker_arguments = []
|
|
57
63
|
if "desktop" in get_docker_type().lower():
|
|
58
64
|
ports = {}
|
|
@@ -61,22 +67,22 @@ def get_container_with_arguments(args: argparse.Namespace , *port_args: PyntDock
|
|
|
61
67
|
docker_type = PyntDockerDesktopContainer(ports=ports)
|
|
62
68
|
else:
|
|
63
69
|
docker_type = PyntNativeContainer(network="host")
|
|
64
|
-
for p in port_args:
|
|
70
|
+
for p in port_args:
|
|
65
71
|
docker_arguments.append(p.name)
|
|
66
72
|
docker_arguments.append(str(p.dest))
|
|
67
|
-
|
|
73
|
+
|
|
68
74
|
if "insecure" in args and args.insecure:
|
|
69
75
|
docker_arguments.append("--insecure")
|
|
70
|
-
|
|
71
|
-
if "application_id" in args and args.application_id:
|
|
72
|
-
docker_arguments += ["--application-id",args.application_id]
|
|
73
76
|
|
|
74
|
-
if "
|
|
75
|
-
docker_arguments += ["--
|
|
77
|
+
if "application_id" in args and args.application_id:
|
|
78
|
+
docker_arguments += ["--application-id", args.application_id]
|
|
76
79
|
|
|
77
|
-
if "
|
|
80
|
+
if "proxy" in args and args.proxy:
|
|
81
|
+
docker_arguments += ["--proxy", args.proxy]
|
|
82
|
+
|
|
83
|
+
if "dev_flags" in args:
|
|
78
84
|
docker_arguments += args.dev_flags.split(" ")
|
|
79
|
-
|
|
85
|
+
|
|
80
86
|
mounts = []
|
|
81
87
|
if "host_ca" in args and args.host_ca:
|
|
82
88
|
ca_name = os.path.basename(args.host_ca)
|
|
@@ -87,38 +93,41 @@ def get_container_with_arguments(args: argparse.Namespace , *port_args: PyntDock
|
|
|
87
93
|
tc_name = os.path.basename(args.transport_config)
|
|
88
94
|
docker_arguments += ["--transport-config", tc_name]
|
|
89
95
|
mounts.append(create_mount(os.path.abspath(args.transport_config), "/etc/pynt/{}".format(tc_name)))
|
|
90
|
-
|
|
91
|
-
env = {
|
|
96
|
+
|
|
97
|
+
env = {PYNT_ID: CredStore().get_tokens(), "PYNT_SAAS_URL": PYNT_SAAS}
|
|
92
98
|
if user_set_all_variables():
|
|
93
99
|
add_env_variables(env)
|
|
94
100
|
return PyntBaseConatiner(docker_type, docker_arguments, mounts, env)
|
|
95
|
-
|
|
101
|
+
|
|
102
|
+
|
|
96
103
|
def _container_image_from_tag(tag: str) -> str:
|
|
97
|
-
if ":" in tag:
|
|
104
|
+
if ":" in tag:
|
|
98
105
|
return tag.split(":")[0]
|
|
99
106
|
|
|
100
107
|
return tag
|
|
101
108
|
|
|
109
|
+
|
|
102
110
|
def user_set_all_variables():
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
return all([PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2])
|
|
112
|
+
|
|
113
|
+
|
|
105
114
|
def add_env_variables(env: dict):
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
env["PYNT_BUCKET_NAME"] = PYNT_BUCKET_NAME
|
|
116
|
+
env["PYNT_PARAM1"] = base64.b64encode(PYNT_PARAM1.encode('utf-8'))
|
|
117
|
+
env["PYNT_PARAM2"] = base64.b64encode(PYNT_PARAM2.encode('utf-8'))
|
|
118
|
+
|
|
119
|
+
|
|
110
120
|
class PyntContainer():
|
|
111
121
|
def __init__(self, image_name, tag, detach, base_container: PyntBaseConatiner) -> None:
|
|
112
122
|
self.docker_client: docker.DockerClient = None
|
|
113
123
|
self.image = image_name if not os.environ.get("IMAGE") else os.environ.get("IMAGE")
|
|
114
124
|
self.tag = tag if not os.environ.get("TAG") else os.environ.get("TAG")
|
|
115
125
|
self.detach = detach
|
|
116
|
-
self.stdout = None
|
|
126
|
+
self.stdout = None
|
|
117
127
|
self.running = False
|
|
118
128
|
self.container_name = ""
|
|
119
129
|
self.base_container = base_container
|
|
120
130
|
|
|
121
|
-
|
|
122
131
|
def _create_docker_client(self):
|
|
123
132
|
self.docker_client = docker.from_env()
|
|
124
133
|
pat = os.environ.get("DOCKER_PASSWORD")
|
|
@@ -126,28 +135,28 @@ class PyntContainer():
|
|
|
126
135
|
registry = os.environ.get("DOCKER_REGISTRY")
|
|
127
136
|
if pat and username and registry:
|
|
128
137
|
self.docker_client.login(username=username, password=pat, registry=registry)
|
|
129
|
-
|
|
138
|
+
|
|
130
139
|
def _is_docker_image_up_to_date(self, image):
|
|
131
140
|
return True
|
|
132
|
-
|
|
141
|
+
|
|
133
142
|
def _handle_outdated_docker_image(self, image):
|
|
134
143
|
return image
|
|
135
|
-
|
|
144
|
+
|
|
136
145
|
def kill_other_instances(self):
|
|
137
146
|
for c in self.docker_client.containers.list():
|
|
138
147
|
if len(c.image.tags) and _container_image_from_tag(c.image.tags[0]) == self.image:
|
|
139
148
|
c.kill()
|
|
140
|
-
|
|
149
|
+
|
|
141
150
|
def stop(self):
|
|
142
151
|
if not self.running:
|
|
143
|
-
return
|
|
152
|
+
return
|
|
144
153
|
|
|
145
154
|
self.kill_other_instances()
|
|
146
155
|
|
|
147
156
|
self.docker_client.close()
|
|
148
157
|
self.docker_client = None
|
|
149
158
|
self.running = False
|
|
150
|
-
|
|
159
|
+
|
|
151
160
|
def is_alive(self):
|
|
152
161
|
if not self.docker_client or not self.container_name:
|
|
153
162
|
return False
|
|
@@ -155,47 +164,48 @@ class PyntContainer():
|
|
|
155
164
|
l = self.docker_client.containers.list(filters={"name": self.container_name})
|
|
156
165
|
if len(l) != 1:
|
|
157
166
|
return False
|
|
158
|
-
|
|
167
|
+
|
|
159
168
|
return l[0].status == "running"
|
|
160
|
-
|
|
169
|
+
|
|
161
170
|
def pull_image(self):
|
|
162
171
|
try:
|
|
163
172
|
return self.docker_client.images.pull(self.image, tag=self.tag)
|
|
164
173
|
except APIError as e:
|
|
165
|
-
analytics.emit(analytics.ERROR,{"error": "Unable to pull image from ghcr: {}".format(e)})
|
|
174
|
+
analytics.emit(analytics.ERROR, {"error": "Unable to pull image from ghcr: {}".format(e)})
|
|
175
|
+
ui_thread.print(ui_thread.PrinterText("Error: Docker unable to pull latest Pynt image due to VPN/proxy. If using a mirror for Docker images, visit docs.pynt.io for help.", ui_thread.PrinterText.WARNING))
|
|
166
176
|
return None
|
|
167
177
|
|
|
168
178
|
def get_image(self):
|
|
169
179
|
try:
|
|
170
180
|
image = self.pull_image()
|
|
171
181
|
if not image:
|
|
172
|
-
|
|
182
|
+
ui_thread.print(ui_thread.PrinterText("Trying to get pynt local image", ui_thread.PrinterText.INFO))
|
|
183
|
+
image = self.docker_client.images.get('{}:{}'.format(self.image, self.tag))
|
|
173
184
|
return image
|
|
174
185
|
except ImageNotFound:
|
|
175
186
|
raise ImageUnavailableException()
|
|
176
187
|
|
|
177
|
-
def run(self):
|
|
188
|
+
def run(self):
|
|
178
189
|
if not self.docker_client:
|
|
179
190
|
self._create_docker_client()
|
|
180
|
-
|
|
191
|
+
|
|
181
192
|
self.running = True
|
|
182
193
|
self.kill_other_instances()
|
|
183
194
|
|
|
184
195
|
ui_thread.print(ui_thread.PrinterText("Pulling latest docker", ui_thread.PrinterText.INFO))
|
|
185
196
|
image = self.get_image()
|
|
186
197
|
ui_thread.print(ui_thread.PrinterText("Docker pull done", ui_thread.PrinterText.INFO))
|
|
187
|
-
|
|
198
|
+
|
|
188
199
|
args = self.base_container.docker_arguments if self.base_container.docker_arguments else None
|
|
189
|
-
|
|
190
200
|
|
|
191
201
|
run_arguments = {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
202
|
+
"image": image,
|
|
203
|
+
"detach": self.detach,
|
|
204
|
+
"mounts": self.base_container.mounts,
|
|
205
|
+
"environment": self.base_container.environment,
|
|
206
|
+
"stream": True,
|
|
207
|
+
"remove": True,
|
|
208
|
+
"command": args
|
|
199
209
|
}
|
|
200
210
|
|
|
201
211
|
run_arguments.update(self.base_container.docker_type.get_argumets())
|
|
@@ -206,13 +216,15 @@ class PyntContainer():
|
|
|
206
216
|
|
|
207
217
|
PyntContainerRegistery.instance().register_container(self)
|
|
208
218
|
|
|
219
|
+
|
|
209
220
|
class PyntDockerDesktopContainer():
|
|
210
221
|
def __init__(self, ports) -> None:
|
|
211
222
|
self.ports = ports
|
|
212
|
-
|
|
223
|
+
|
|
213
224
|
def get_argumets(self):
|
|
214
225
|
return {"ports": self.ports} if self.ports else {}
|
|
215
|
-
|
|
226
|
+
|
|
227
|
+
|
|
216
228
|
class PyntNativeContainer():
|
|
217
229
|
def __init__(self, network) -> None:
|
|
218
230
|
self.network = network
|
|
@@ -230,13 +242,13 @@ class PyntContainerRegistery():
|
|
|
230
242
|
@staticmethod
|
|
231
243
|
def instance():
|
|
232
244
|
if not PyntContainerRegistery._instance:
|
|
233
|
-
PyntContainerRegistery._instance = PyntContainerRegistery()
|
|
245
|
+
PyntContainerRegistery._instance = PyntContainerRegistery()
|
|
234
246
|
|
|
235
247
|
return PyntContainerRegistery._instance
|
|
236
248
|
|
|
237
249
|
def register_container(self, c: PyntContainer):
|
|
238
|
-
self.containers.append(c)
|
|
239
|
-
|
|
250
|
+
self.containers.append(c)
|
|
251
|
+
|
|
240
252
|
def stop_all_containers(self):
|
|
241
|
-
for c in self.containers:
|
|
253
|
+
for c in self.containers:
|
|
242
254
|
c.stop()
|
pyntcli/store/store.py
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
from typing import Type
|
|
2
|
-
import os
|
|
3
|
-
import platform
|
|
1
|
+
from typing import Type
|
|
2
|
+
import os
|
|
4
3
|
|
|
5
4
|
from .json_connector import JsonStoreConnector
|
|
6
5
|
from .store_connector import StoreConnector
|
|
7
6
|
|
|
7
|
+
|
|
8
8
|
class Store():
|
|
9
|
-
def __init__(self, file_location: str, connector_type: Type[StoreConnector]) -> None
|
|
9
|
+
def __init__(self, file_location: str, connector_type: Type[StoreConnector]) -> None:
|
|
10
10
|
self.file_location = file_location
|
|
11
|
-
self.connector:StoreConnector = None
|
|
11
|
+
self.connector: StoreConnector = None
|
|
12
12
|
self._file = None
|
|
13
13
|
self._connector_tpye = connector_type
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def _get_file_data(self):
|
|
16
|
-
if self.connector:
|
|
17
|
-
return
|
|
16
|
+
if self.connector:
|
|
17
|
+
return
|
|
18
18
|
|
|
19
19
|
dirname = os.path.dirname(self.file_location)
|
|
20
20
|
if not os.path.exists(dirname):
|
|
21
21
|
os.makedirs(dirname)
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
if not os.path.exists(self.file_location):
|
|
24
24
|
with open(self.file_location, "w") as f:
|
|
25
25
|
self.connector = self._connector_tpye(self._connector_tpye.default_value())
|
|
@@ -35,24 +35,29 @@ class Store():
|
|
|
35
35
|
def put(self, key, value):
|
|
36
36
|
self._get_file_data()
|
|
37
37
|
self.connector.put(key, value)
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
def get_path(self):
|
|
40
40
|
return self.file_location
|
|
41
41
|
|
|
42
42
|
def __enter__(self):
|
|
43
43
|
self._get_file_data()
|
|
44
|
-
return self
|
|
44
|
+
return self
|
|
45
45
|
|
|
46
|
-
def __exit__(self, type, value, traceback):
|
|
46
|
+
def __exit__(self, type, value, traceback):
|
|
47
47
|
with open(self.file_location, "w") as f:
|
|
48
48
|
f.write(self.connector.dump())
|
|
49
49
|
|
|
50
|
+
|
|
50
51
|
class CredStore(Store):
|
|
51
52
|
def __init__(self) -> None:
|
|
52
53
|
dir = ".pynt"
|
|
53
54
|
super().__init__(file_location=os.path.join(os.path.expanduser("~"), dir, "creds.json"),
|
|
54
55
|
connector_type=JsonStoreConnector)
|
|
55
|
-
|
|
56
|
+
|
|
56
57
|
def get_access_token(self):
|
|
57
58
|
return self.get("token")["access_token"]
|
|
58
|
-
|
|
59
|
+
|
|
60
|
+
def get_tokens(self):
|
|
61
|
+
all_tokens = self.get("token")
|
|
62
|
+
token_to_json_string = '{"token":'+str(all_tokens).replace("\'", "\"")+"}"
|
|
63
|
+
return token_to_json_string
|