pyntcli 0.1.116__py3-none-any.whl → 0.1.118__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.116"
1
+ __version__ = "0.1.118"
pyntcli/auth/login.py CHANGED
@@ -31,13 +31,6 @@ PYNT_SAAS = os.environ.get("PYNT_SAAS_URL") if os.environ.get(
31
31
  "PYNT_SAAS_URL") else "https://api.pynt.io/v1"
32
32
  PYNT_APP_URL = os.environ.get("PYNT_APP_URL") if os.environ.get(
33
33
  "PYNT_APP_URL") else "https://app.pynt.io"
34
- PYNT_BUCKET_NAME = os.environ.get(
35
- "PYNT_BUCKET_NAME") if os.environ.get("PYNT_BUCKET_NAME") else ""
36
- PYNT_PARAM1 = os.environ.get(
37
- "PYNT_PARAM1") if os.environ.get("PYNT_PARAM1") else ""
38
- PYNT_PARAM2 = os.environ.get(
39
- "PYNT_PARAM2") if os.environ.get("PYNT_PARAM2") else ""
40
-
41
34
 
42
35
  def generate_device_code() -> str:
43
36
  """
@@ -98,7 +98,8 @@ class CommandSubCommand(sub_command.PyntSubCommand):
98
98
  env_copy.update(
99
99
  {
100
100
  "http_proxy": "http://localhost:{}".format(args.proxy_port),
101
- "https_proxy": "http://localhost:{}".format(args.proxy_port)
101
+ "https_proxy": "http://localhost:{}".format(args.proxy_port),
102
+ "JAVA_TOOL_OPTIONS": "-Dhttp.proxyHost=localhost -Dhttp.proxyPort={} -Dhttps.proxyHost=localhost -Dhttps.proxyPort={}".format(args.proxy_port, args.proxy_port),
102
103
  }
103
104
  )
104
105
  return env_copy
@@ -1,5 +1,4 @@
1
1
  import argparse
2
- import requests
3
2
  from typing import Dict, List
4
3
  from datetime import datetime, timedelta
5
4
  from pyntcli import __version__ as cli_version
@@ -134,30 +133,36 @@ class PyntCommand:
134
133
  return False
135
134
 
136
135
  def _post_login_args_validation(self, args: argparse.Namespace, command: str, is_business_plan_user: bool):
136
+ if not is_business_plan_user:
137
+ # All other validations only relevant for business plan users
138
+ return
139
+
140
+ if command in commands_without_app_id:
141
+ # Skip application validation if it isn't required
142
+ return
143
+
144
+
137
145
  if getattr(args, "application_name"):
138
- try:
139
- pynt_client.get_application_by_name(args.application_name)
140
- except HTTPError as e:
141
- if e.response.status_code == 404:
142
- if getattr(args, "yes") or self._is_auto_create_app_confirmed(args.application_name):
143
- return
144
- else:
145
- raise UserAbortedException()
146
- if getattr(args, "yes") or self._is_missing_app_id_confirmed():
146
+ # When `--application-name` exists, we'll validate its existence.
147
+ # If the validation fails due to some error, we'll throw it back as we can't continue the execution.
148
+ #
149
+ # In any case, when `application-name` is provided, we don't need `application-id`
150
+ if pynt_client.validate_application_name_exists(args.application_name):
151
+ return
152
+
153
+ # Application does not exist, validate if the user wants to create it automatically
154
+ if getattr(args, "yes") or self._is_auto_create_app_confirmed(args.application_name):
147
155
  return
148
156
  else:
149
157
  raise UserAbortedException()
150
158
 
151
- # Confirm not using application-id flag if applicable
152
- if getattr(args, "application_id") or command in commands_without_app_id:
159
+ if getattr(args, "application_id"):
153
160
  return
154
161
 
155
- # For a business user, it's recommended to always provide the application-id.
156
- if is_business_plan_user:
157
- if getattr(args, "yes") or self._is_missing_app_id_confirmed():
158
- return
159
- else:
160
- raise UserAbortedException()
162
+ if getattr(args, "yes") or self._is_missing_app_id_confirmed():
163
+ return
164
+ else:
165
+ raise UserAbortedException()
161
166
 
162
167
  def is_confirmed(self, prompt_history_key: str, confirmation_message: str, default_confirmation: str) -> bool:
163
168
  """
@@ -195,5 +200,5 @@ class PyntCommand:
195
200
 
196
201
 
197
202
  def _is_auto_create_app_confirmed(self, application_name: str) -> bool:
198
- return self.is_confirmed("", f"Application {application_name} will be created automatically if it does not exist.\n" +
203
+ return self.is_confirmed("", f"Application {application_name} will be created automatically as it does not exist.\n" +
199
204
  f"Do you want to continue with the application name {application_name}?", "yes")
@@ -1,7 +1,53 @@
1
-
2
1
  import socket
2
+ import subprocess
3
+ import time
4
+ from typing import Optional, List
5
+
6
+ GRACEFUL_KILL_TIMEOUT_SECONDS = 10
3
7
 
4
8
 
5
9
  def is_port_in_use(port: int) -> bool:
6
10
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
7
- return s.connect_ex(('localhost', port)) == 0
11
+ return s.connect_ex(("localhost", port)) == 0
12
+
13
+
14
+ def list_running_containers(
15
+ name: Optional[str] = None, container_id: Optional[str] = None
16
+ ) -> List[str]:
17
+ ps_filter = []
18
+ if name is not None:
19
+ ps_filter.extend(["-f", f"name={name}"])
20
+ elif container_id is not None:
21
+ ps_filter.extend(["-f", f"id={container_id}"])
22
+
23
+ output = subprocess.check_output(
24
+ ["docker", "ps", "-a", "-q", *ps_filter],
25
+ text=True,
26
+ )
27
+ return output.splitlines()
28
+
29
+
30
+ def kill_container_gracefully(container_id: str):
31
+ subprocess.run(
32
+ ["docker", "kill", "--signal", "SIGINT", container_id],
33
+ stdout=subprocess.DEVNULL,
34
+ stderr=subprocess.DEVNULL,
35
+ )
36
+
37
+ for _ in range(GRACEFUL_KILL_TIMEOUT_SECONDS):
38
+ time.sleep(1)
39
+ is_still_running = len(list_running_containers(container_id=container_id)) > 0
40
+ if not is_still_running:
41
+ return
42
+
43
+ if len(list_running_containers(container_id=container_id)) > 0:
44
+ kill_container_gracefully(container_id)
45
+
46
+
47
+ def kill_container(container_id: str):
48
+ # `doccker remove -f` sends SIGKILL
49
+ subprocess.run(
50
+ ["docker", "remove", "-f", container_id],
51
+ stdout=subprocess.DEVNULL,
52
+ stderr=subprocess.DEVNULL,
53
+ )
@@ -7,17 +7,15 @@ import argparse
7
7
  import threading
8
8
  from datetime import datetime
9
9
  from typing import List
10
- import base64
11
10
 
12
11
  from . import container_utils
13
12
 
14
13
  from pyntcli.ui import ui_thread
15
14
  from pyntcli.analytics import send as analytics
16
15
  from pyntcli.store import CredStore
17
- from pyntcli.auth.login import PYNT_ID, PYNT_SAAS, PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2
16
+ from pyntcli.auth.login import PYNT_ID, PYNT_SAAS
18
17
 
19
18
  PYNT_DOCKER_IMAGE = "ghcr.io/pynt-io/pynt"
20
- IMAGE_TAGS = ["postman-latest", "newman-latest", "har-latest", "proxy-latest", "v1-latest"]
21
19
 
22
20
 
23
21
  def create_mount(src, destination, mount_type="bind"):
@@ -95,8 +93,9 @@ class DockerContainerConfig:
95
93
  self.docker_arguments = build_docker_args(integration_name, args, port_args)
96
94
  self.mounts = get_docker_mounts(args)
97
95
  self.env_vars = {PYNT_ID: CredStore().get_tokens(), "PYNT_SAAS_URL": PYNT_SAAS}
98
- if user_set_all_variables():
99
- add_env_variables(self.env_vars)
96
+ otel_endpoint = os.environ.get("OTEL_COLLECTOR_ENDPOINT")
97
+ if otel_endpoint:
98
+ self.env_vars["OTEL_COLLECTOR_ENDPOINT"] = otel_endpoint
100
99
 
101
100
 
102
101
  def get_image_config(args: argparse.Namespace) -> PyntDockerImage:
@@ -122,16 +121,6 @@ def is_network_host() -> bool:
122
121
  return True
123
122
 
124
123
 
125
- def user_set_all_variables():
126
- return all([PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2])
127
-
128
-
129
- def add_env_variables(env: dict):
130
- env["PYNT_BUCKET_NAME"] = PYNT_BUCKET_NAME
131
- env["PYNT_PARAM1"] = base64.b64encode(PYNT_PARAM1.encode('utf-8')).decode('utf-8')
132
- env["PYNT_PARAM2"] = base64.b64encode(PYNT_PARAM2.encode('utf-8')).decode('utf-8')
133
-
134
-
135
124
  def value_from_environment_variable(key):
136
125
  e = os.environ.get(key)
137
126
 
@@ -300,29 +289,23 @@ class PyntContainerNative:
300
289
  # Start log streaming in a separate thread
301
290
  DockerLogFollower(docker_exec, container_id).start()
302
291
 
303
- def kill_other_instances(self, report_to_user=True):
292
+ def kill_other_instances(self, gracefully: bool) -> int:
304
293
  ui_thread.print_verbose("Killing other pynt containers if such exist")
294
+ killed_containers = 0
305
295
  try:
306
- for _ in IMAGE_TAGS:
307
- command = ["docker", "ps", "-a", "-q", "-f", f"name={self.container_name}"]
308
- containers_output = subprocess.check_output(command, text=True)
309
- if not containers_output:
310
- continue
311
-
312
- container_ids = containers_output.splitlines()
313
- for container_id in container_ids:
314
- kill_command = ["docker", "kill", container_id]
315
- remove_command = ["docker", "remove", container_id]
316
- subprocess.run(kill_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
317
- subprocess.run(remove_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
318
- if report_to_user:
319
- ui_thread.print(
320
- ui_thread.PrinterText("Another Pynt container was running, killed it", ui_thread.PrinterText))
321
-
296
+ container_ids = container_utils.list_running_containers(name=self.container_name)
297
+ for container_id in container_ids:
298
+ if gracefully:
299
+ container_utils.kill_container_gracefully(container_id)
300
+ else:
301
+ container_utils.kill_container(container_id)
302
+ killed_containers += 1
322
303
  except subprocess.CalledProcessError:
323
304
  analytics.emit(analytics.ERROR, {"error": "Unable to kill other pynt containers"})
324
305
  ui_thread.print(ui_thread.PrinterText("Error: Unable to kill other pynt containers", ui_thread.PrinterText.WARNING))
325
306
 
307
+ return killed_containers
308
+
326
309
  def fetch_and_validate_image(self):
327
310
  try:
328
311
  ui_thread.print(ui_thread.PrinterText("Pulling latest docker image", ui_thread.PrinterText.INFO))
@@ -356,11 +339,18 @@ class PyntContainerNative:
356
339
  def stop(self):
357
340
  if not self.running:
358
341
  return
359
- self.kill_other_instances(report_to_user=False)
342
+ self.kill_other_instances(gracefully=True)
360
343
  self.running = False
361
344
 
362
345
  def pre_run_validation(self, port):
363
- self.kill_other_instances()
346
+ killed_containers = self.kill_other_instances(gracefully=False)
347
+ if killed_containers > 0:
348
+ ui_thread.print(
349
+ ui_thread.PrinterText(
350
+ "Another Pynt container was running, killed it",
351
+ ui_thread.PrinterText,
352
+ )
353
+ )
364
354
 
365
355
  ui_thread.print_verbose("Checking if port is in use")
366
356
  if container_utils.is_port_in_use(int(port)):
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import os
2
3
  import requests
3
4
 
@@ -5,7 +6,6 @@ import pyntcli.log.log as log
5
6
  from pyntcli.store import CredStore
6
7
 
7
8
  PYNT_SAAS = os.environ.get("PYNT_SAAS_URL", "https://api.pynt.io/v1")
8
- logger = log.get_logger()
9
9
 
10
10
  class PyntClient:
11
11
  def __init__(self, base_url=PYNT_SAAS):
@@ -29,28 +29,29 @@ class PyntClient:
29
29
  return response.json() # returning actual data
30
30
 
31
31
  except requests.exceptions.HTTPError as e:
32
- logger.error(f"HTTP error accessing '{url}': {e}")
33
32
  raise e
34
- except requests.exceptions.RequestException as e:
35
- logger.error(f"Error accessing '{url}': {e}")
33
+ except requests.exceptions.RequestException:
34
+ pass
36
35
 
37
36
  return None
38
37
 
39
- def get_application_by_name(self, application_name):
40
- url = f"{self.base_url}/application/{application_name}"
38
+ def validate_application_name_exists(self, application_name):
39
+ url = f"{self.base_url}/application"
41
40
  headers = self._get_headers()
42
-
43
- try:
44
- response = requests.get(url, headers=headers)
45
- response.raise_for_status()
46
- return response.json() # returning actual data
47
-
48
- except requests.exceptions.HTTPError as e:
49
- logger.error(f"HTTP error accessing '{url}': {e}")
50
- raise e
51
- except requests.exceptions.RequestException as e:
52
- logger.error(f"Error accessing '{url}': {e}")
53
-
54
- return None
41
+ query_filter = {
42
+ "filter": json.dumps({
43
+ "where": {
44
+ "name": application_name,
45
+ },
46
+ })
47
+ }
48
+
49
+ applications_response = requests.get(url, headers=headers, params=query_filter)
50
+ if applications_response.status_code == 404:
51
+ return False
52
+ applications_response.raise_for_status()
53
+
54
+ applications = applications_response.json()
55
+ return len(applications) == 1
55
56
 
56
57
  pynt_client = PyntClient()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyntcli
3
- Version: 0.1.116
3
+ Version: 0.1.118
4
4
  Summary: Command line utility to handle all of Pynt's different integrations
5
5
  Author-email: Pynt-io <support@pynt.io>
6
6
  Project-URL: Homepage, https://pynt.io
@@ -1,21 +1,21 @@
1
1
  ignoreTests/conftest.py,sha256=gToq5K74GtgeGQXjFvXSzMaE6axBYxAzcFG5XJPOXjI,427
2
2
  ignoreTests/auth/login.py,sha256=7GeBirHTD9t6EassLYsegCw1FZHkfjvVW1Z5uybHzgM,3801
3
3
  ignoreTests/store/cred_store.py,sha256=_7-917EtNC9eKEumO2_lt-7KuDmCwOZFaowCm7DbA_A,254
4
- pyntcli/__init__.py,sha256=WH7k6Lp9d5LeVrO9NrIvNHTU68a2Yw4l0K-_6mGJeDk,24
4
+ pyntcli/__init__.py,sha256=NPyYE62Vvtk0QcfoU6DNt1tjRHKRzegMxg0JMV_i078,24
5
5
  pyntcli/main.py,sha256=RD0W2_0ogYBCXubo-YewxHYkiIXxNv6NkZOh3n1VujE,5964
6
6
  pyntcli/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  pyntcli/analytics/send.py,sha256=0hJ0WJNFHLqyohtRr_xOg5WEXzxHrUOlcePPg-k65Hk,3846
8
8
  pyntcli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- pyntcli/auth/login.py,sha256=Oz1DtEZje0afgJcHSwEFHcH78X-QD5Mjn0CQ9ZgCfQU,5844
9
+ pyntcli/auth/login.py,sha256=qtdoCgWIPi3_YXehI7SVk_E8aDVhH39wGc87tlkazSE,5558
10
10
  pyntcli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  pyntcli/commands/burp.py,sha256=9kcqjC9Rgj5oZimGZuA_oO71Hn2Z3py8k5vr-eMKnUM,14317
12
- pyntcli/commands/command.py,sha256=ulxhWyTnuqQfyNy5somqBbQoTCvd0Hg4OnPJ8thJzAs,11078
12
+ pyntcli/commands/command.py,sha256=TuX7It8gOSG8fY6RvpAak3bkvmKKy-3k7kw-SI8xvKw,11260
13
13
  pyntcli/commands/har.py,sha256=laVRjG35LDEykoSfRIuEwHYfLstui6EpPxSjcam64ng,4719
14
14
  pyntcli/commands/id_command.py,sha256=UBEgMIpm4vauTCsKyixltiGUolNg_OfHEJvJ_i5BpJY,943
15
15
  pyntcli/commands/listen.py,sha256=RTlUt2oq5_BPeBZpG5yQ6GPAG_Cf0JVrUrkzz8uw9ns,9042
16
16
  pyntcli/commands/newman.py,sha256=w0d0Pi1swJQ8E3oRs6vsgw_591sl-4bcLpWZwuBXmDI,5282
17
17
  pyntcli/commands/postman.py,sha256=kN1PkzPpcUnsbirg17AN9JwJZu6VBt6OeKYrLGrvtFc,4964
18
- pyntcli/commands/pynt_cmd.py,sha256=TcsKVl0QZLEDIZjo-977IY6k4MAXVk_hljraW1eJmyg,7790
18
+ pyntcli/commands/pynt_cmd.py,sha256=oRKgiG9i84sxkS-C5Nw4vv9mIO6zL8YfqhOUJ-XVpas,7934
19
19
  pyntcli/commands/root.py,sha256=bTGlFroeK7WG9dAhFkVTVnpEM-Gdp8QKCuTxW_QWNlQ,4441
20
20
  pyntcli/commands/static_file_extensions.py,sha256=PZJb02BI-64tbU-j3rdCNsXzTh7gkIDGxGKbKNw3h5k,1995
21
21
  pyntcli/commands/sub_command.py,sha256=GF3-rE_qk2L4jGPFqHLm9SdGINmu3EakhjJTFyWjRms,374
@@ -24,10 +24,10 @@ pyntcli/commands/util.py,sha256=ev06aUlJ9tCSw1eAGVIsGlVVY4u0H6AJG9b5MB_bAzs,4594
24
24
  pyntcli/log/__init__.py,sha256=cOGwOYzMoshEbZiiasBGkj6wF0SBu3Jdpl-AuakDesw,19
25
25
  pyntcli/log/log.py,sha256=YXCvcCzuhQ5QUT2L02uQEdN_lTCzLEuet4OnLuEnjlM,112
26
26
  pyntcli/pynt_docker/__init__.py,sha256=PQIOVxc7XXtMLfEX7ojgwf_Z3mmTllO3ZvzUZTPOxQY,30
27
- pyntcli/pynt_docker/container_utils.py,sha256=_Onn7loInzyJAG2-Uk6CGpsuRyelmUFHOvtJj4Uzi9A,175
28
- pyntcli/pynt_docker/pynt_container.py,sha256=4vW12inRuKgsUM5K8NHTEjPZE1Qp8F62_J0gy6FjiRU,14506
27
+ pyntcli/pynt_docker/container_utils.py,sha256=DeI-uSgdcO_2rGs2dvQ5gBDNo_iKKuPIQO2D9oej5Gw,1485
28
+ pyntcli/pynt_docker/pynt_container.py,sha256=7VG7e8mCabFUJoX-AuSnxVoXtO9ben-QQdNBL5Y5z9g,13861
29
29
  pyntcli/saas_client/__init__.py,sha256=HPBzoC5a6F5_WkHubcjq_W4m1OQ9i0TX8QXBtJlKm1M,26
30
- pyntcli/saas_client/saas_client.py,sha256=7mJNo2x4Oy2N4NKdHyGRxXZUk_VHZvRu1x586rojrp0,1756
30
+ pyntcli/saas_client/saas_client.py,sha256=Cpf1pitVlxjW7KpchPL7Q-DgUqw6A8ldCtDlBXaPig0,1663
31
31
  pyntcli/store/__init__.py,sha256=1fP8cEAQCF_myja3gnhHH9FEqtBiOJ-2aBmUXSKBdFA,41
32
32
  pyntcli/store/json_connector.py,sha256=UGs3uORw3iyn0YJ8kzab-veEZToA6d-ByXYuqEleWsA,560
33
33
  pyntcli/store/store.py,sha256=Kl_HT9FJFdKlAH7SwAt3g4-bW-r-1ve_u13OPggQai0,2529
@@ -42,8 +42,8 @@ pyntcli/ui/report.py,sha256=W-icPSZrGLOubXgam0LpOvHLl_aZg9Zx9qIkL8Ym5PE,1930
42
42
  pyntcli/ui/ui_thread.py,sha256=XUBgLpYQjVhrilU-ofw7VSXgTiwneSdTxm61EvC3x4Q,5091
43
43
  tests/test_utils.py,sha256=t5fTQUk1U_Js6iMxcGYGqt4C-crzOJ0CqCKtLkRtUi0,2050
44
44
  tests/commands/test_pynt_cmd.py,sha256=J4JrEuD_qSVN76Fu6bKRjrxWSwCTXVEAzVPYdXMa0tI,8826
45
- pyntcli-0.1.116.dist-info/METADATA,sha256=68McFg0rYxmguHJX3Tc-YVRv9pg-zyyxNuUN1H_283M,427
46
- pyntcli-0.1.116.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
47
- pyntcli-0.1.116.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
48
- pyntcli-0.1.116.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
49
- pyntcli-0.1.116.dist-info/RECORD,,
45
+ pyntcli-0.1.118.dist-info/METADATA,sha256=0FTF3QHwBL7UzwrFnPrvSNjSO_A0D6XMPS_tPi-N3xM,427
46
+ pyntcli-0.1.118.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
+ pyntcli-0.1.118.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
48
+ pyntcli-0.1.118.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
49
+ pyntcli-0.1.118.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5