portforward 0.6.1__tar.gz → 0.7.0__tar.gz

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 portforward might be problematic. Click here for more details.

Files changed (37) hide show
  1. portforward-0.7.0/.github/workflows/release.yml +120 -0
  2. {portforward-0.6.1 → portforward-0.7.0}/.readthedocs.yml +5 -1
  3. {portforward-0.6.1 → portforward-0.7.0}/Cargo.lock +1 -1
  4. {portforward-0.6.1 → portforward-0.7.0}/Cargo.toml +1 -1
  5. {portforward-0.6.1 → portforward-0.7.0}/HISTORY.rst +12 -0
  6. {portforward-0.6.1 → portforward-0.7.0}/Makefile +4 -4
  7. {portforward-0.6.1 → portforward-0.7.0}/PKG-INFO +6 -11
  8. {portforward-0.6.1 → portforward-0.7.0}/README.rst +4 -9
  9. {portforward-0.6.1 → portforward-0.7.0}/docs/modules.rst +1 -1
  10. {portforward-0.6.1 → portforward-0.7.0}/pyproject.toml +2 -2
  11. {portforward-0.6.1 → portforward-0.7.0}/python/portforward/__init__.py +35 -13
  12. {portforward-0.6.1 → portforward-0.7.0}/python/portforward/_portforward.pyi +1 -1
  13. {portforward-0.6.1 → portforward-0.7.0}/src/lib.rs +2 -2
  14. {portforward-0.6.1 → portforward-0.7.0}/src/portforward.rs +9 -6
  15. {portforward-0.6.1 → portforward-0.7.0}/tests/test_portforward.py +40 -1
  16. {portforward-0.6.1 → portforward-0.7.0}/.editorconfig +0 -0
  17. {portforward-0.6.1 → portforward-0.7.0}/.github/ISSUE_TEMPLATE.md +0 -0
  18. {portforward-0.6.1 → portforward-0.7.0}/.github/workflows/python-app.yml +0 -0
  19. {portforward-0.6.1 → portforward-0.7.0}/.gitignore +0 -0
  20. {portforward-0.6.1 → portforward-0.7.0}/AUTHORS.rst +0 -0
  21. {portforward-0.6.1 → portforward-0.7.0}/CONTRIBUTING.rst +0 -0
  22. {portforward-0.6.1 → portforward-0.7.0}/LICENSE +0 -0
  23. {portforward-0.6.1 → portforward-0.7.0}/docs/Makefile +0 -0
  24. {portforward-0.6.1 → portforward-0.7.0}/docs/authors.rst +0 -0
  25. {portforward-0.6.1 → portforward-0.7.0}/docs/conf.py +0 -0
  26. {portforward-0.6.1 → portforward-0.7.0}/docs/contributing.rst +0 -0
  27. {portforward-0.6.1 → portforward-0.7.0}/docs/docs/conf.rst +0 -0
  28. {portforward-0.6.1 → portforward-0.7.0}/docs/docs/modules.rst +0 -0
  29. {portforward-0.6.1 → portforward-0.7.0}/docs/history.rst +0 -0
  30. {portforward-0.6.1 → portforward-0.7.0}/docs/index.rst +0 -0
  31. {portforward-0.6.1 → portforward-0.7.0}/docs/installation.rst +0 -0
  32. {portforward-0.6.1 → portforward-0.7.0}/docs/make.bat +0 -0
  33. {portforward-0.6.1 → portforward-0.7.0}/docs/portforward.rst +0 -0
  34. {portforward-0.6.1 → portforward-0.7.0}/python/portforward/py.typed +0 -0
  35. {portforward-0.6.1 → portforward-0.7.0}/requirements-dev.txt +0 -0
  36. {portforward-0.6.1 → portforward-0.7.0}/tests/conftest.py +0 -0
  37. {portforward-0.6.1 → portforward-0.7.0}/tests/resources.yaml +0 -0
@@ -0,0 +1,120 @@
1
+ # This file is autogenerated by maturin v0.14.17
2
+ # To update, run
3
+ #
4
+ # maturin generate-ci github
5
+ #
6
+ name: Release
7
+
8
+ on:
9
+ push:
10
+ tags:
11
+ - 'v*'
12
+ # pull_request:
13
+ workflow_dispatch:
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ linux:
20
+ runs-on: ubuntu-latest
21
+ strategy:
22
+ matrix:
23
+ target: [x86_64, x86, aarch64]
24
+ version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
25
+ steps:
26
+ - uses: actions/checkout@v3
27
+ - uses: actions/setup-python@v4
28
+ with:
29
+ python-version: ${{ matrix.version }}
30
+ - name: Build wheels
31
+ uses: PyO3/maturin-action@v1
32
+ with:
33
+ target: ${{ matrix.target }}
34
+ args: --release --out dist --find-interpreter
35
+ sccache: 'true'
36
+ manylinux: auto
37
+ - name: Upload wheels
38
+ uses: actions/upload-artifact@v3
39
+ with:
40
+ name: wheels
41
+ path: dist
42
+
43
+ windows:
44
+ runs-on: windows-latest
45
+ strategy:
46
+ matrix:
47
+ target: [x64, x86]
48
+ version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
49
+ steps:
50
+ - uses: actions/checkout@v3
51
+ - uses: actions/setup-python@v4
52
+ with:
53
+ python-version: ${{ matrix.version }}
54
+ architecture: ${{ matrix.target }}
55
+ - name: Build wheels
56
+ uses: PyO3/maturin-action@v1
57
+ with:
58
+ target: ${{ matrix.target }}
59
+ args: --release --out dist --find-interpreter
60
+ sccache: 'true'
61
+ - name: Upload wheels
62
+ uses: actions/upload-artifact@v3
63
+ with:
64
+ name: wheels
65
+ path: dist
66
+
67
+ macos:
68
+ runs-on: macos-latest
69
+ strategy:
70
+ matrix:
71
+ target: [x86_64, aarch64]
72
+ version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
73
+ steps:
74
+ - uses: actions/checkout@v3
75
+ - uses: actions/setup-python@v4
76
+ with:
77
+ python-version: ${{ matrix.version }}
78
+ - name: Build wheels
79
+ uses: PyO3/maturin-action@v1
80
+ with:
81
+ target: ${{ matrix.target }}
82
+ args: --release --out dist --find-interpreter
83
+ sccache: 'true'
84
+ - name: Upload wheels
85
+ uses: actions/upload-artifact@v3
86
+ with:
87
+ name: wheels
88
+ path: dist
89
+
90
+ sdist:
91
+ runs-on: ubuntu-latest
92
+ steps:
93
+ - uses: actions/checkout@v3
94
+ - name: Build sdist
95
+ uses: PyO3/maturin-action@v1
96
+ with:
97
+ command: sdist
98
+ args: --out dist
99
+ - name: Upload sdist
100
+ uses: actions/upload-artifact@v3
101
+ with:
102
+ name: wheels
103
+ path: dist
104
+
105
+ release:
106
+ name: Release
107
+ runs-on: ubuntu-latest
108
+ if: "startsWith(github.ref, 'refs/tags/')"
109
+ needs: [linux, windows, macos, sdist]
110
+ steps:
111
+ - uses: actions/download-artifact@v3
112
+ with:
113
+ name: wheels
114
+ - name: Publish to PyPI
115
+ uses: PyO3/maturin-action@v1
116
+ env:
117
+ MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
118
+ with:
119
+ command: upload
120
+ args: --skip-existing *
@@ -1,5 +1,10 @@
1
1
  version: 2
2
2
 
3
+ build:
4
+ os: "ubuntu-22.04"
5
+ tools:
6
+ python: "3.10"
7
+
3
8
  # Build documentation in the docs/ directory with Sphinx
4
9
  sphinx:
5
10
  configuration: docs/conf.py
@@ -10,6 +15,5 @@ formats:
10
15
 
11
16
  # Optionally set the version of Python and requirements required to build your docs
12
17
  python:
13
- version: "3.8"
14
18
  install:
15
19
  - requirements: requirements-dev.txt
@@ -1142,7 +1142,7 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
1142
1142
 
1143
1143
  [[package]]
1144
1144
  name = "portforward"
1145
- version = "0.6.1"
1145
+ version = "0.7.0"
1146
1146
  dependencies = [
1147
1147
  "anyhow",
1148
1148
  "env_logger",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "portforward"
3
- version = "0.6.1"
3
+ version = "0.7.0"
4
4
  edition = "2021"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -2,6 +2,18 @@
2
2
  History
3
3
  =======
4
4
 
5
+ 0.7.0 (2024-10-18)
6
+ ------------------
7
+ * Allow binding to a local random free port
8
+
9
+ 0.6.2 (2024-06-19)
10
+ ------------------
11
+ * Allow defining binding ip
12
+
13
+ 0.6.1 (2024-01-25)
14
+ ------------------
15
+ * Fixed wrong logger setup
16
+
5
17
  0.6.0 (2023-06-13)
6
18
  ------------------
7
19
  * Rewrite native part in Rust with support of Py03 and maturin
@@ -66,36 +66,36 @@ servedocs: docs ## compile the docs watching for changes
66
66
 
67
67
  release-linux: clean ## creates and release linux wheels
68
68
  mkdir -p dist
69
- docker run --rm -v $(PWD):/io ghcr.io/pyo3/maturin build --release -i python3.7 --out dist --strip
70
69
  docker run --rm -v $(PWD):/io ghcr.io/pyo3/maturin build --release -i python3.8 --out dist --strip
71
70
  docker run --rm -v $(PWD):/io ghcr.io/pyo3/maturin build --release -i python3.9 --out dist --strip
72
71
  docker run --rm -v $(PWD):/io ghcr.io/pyo3/maturin build --release -i python3.10 --out dist --strip
73
72
  docker run --rm -v $(PWD):/io ghcr.io/pyo3/maturin build --release -i python3.11 --out dist --strip
73
+ docker run --rm -v $(PWD):/io ghcr.io/pyo3/maturin build --release -i python3.12 --out dist --strip
74
74
 
75
75
  maturin sdist --out dist
76
76
 
77
77
  twine upload dist/*
78
78
 
79
79
  release-macos: clean ## creates and release macos wheels
80
- maturin build --release --target aarch64-apple-darwin --zig -i python3.7 --out dist --strip
81
80
  maturin build --release --target aarch64-apple-darwin --zig -i python3.8 --out dist --strip
82
81
  maturin build --release --target aarch64-apple-darwin --zig -i python3.9 --out dist --strip
83
82
  maturin build --release --target aarch64-apple-darwin --zig -i python3.10 --out dist --strip
84
83
  maturin build --release --target aarch64-apple-darwin --zig -i python3.11 --out dist --strip
84
+ maturin build --release --target aarch64-apple-darwin --zig -i python3.12 --out dist --strip
85
85
 
86
- maturin build --release -i python3.7 --out dist --strip
87
86
  maturin build --release -i python3.8 --out dist --strip
88
87
  maturin build --release -i python3.9 --out dist --strip
89
88
  maturin build --release -i python3.10 --out dist --strip
90
89
  maturin build --release -i python3.11 --out dist --strip
90
+ maturin build --release -i python3.12 --out dist --strip
91
91
 
92
92
  twine upload dist/*
93
93
 
94
94
  release-windows: clean ## creates and release window wheels
95
- maturin build --release -i python37 --out dist --strip
96
95
  maturin build --release -i python38 --out dist --strip
97
96
  maturin build --release -i python39 --out dist --strip
98
97
  maturin build --release -i python310 --out dist --strip
99
98
  maturin build --release -i python311 --out dist --strip
99
+ maturin build --release -i python312 --out dist --strip
100
100
 
101
101
  twine upload dist/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portforward
3
- Version: 0.6.1
3
+ Version: 0.7.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -19,8 +19,8 @@ License: MIT License
19
19
  Requires-Python: >=3.7
20
20
  Description-Content-Type: text/x-rst; charset=UTF-8
21
21
  Project-URL: Documentation, https://portforward.readthedocs.io
22
- Project-URL: Repository, https://github.com/pytogo/portforward.git
23
22
  Project-URL: Changelog, https://github.com/pytogo/portforward/blob/main/HISTORY.rst
23
+ Project-URL: Repository, https://github.com/pytogo/portforward.git
24
24
 
25
25
  ===========
26
26
  portforward
@@ -58,22 +58,17 @@ Installation
58
58
 
59
59
  Wheels are available for:
60
60
 
61
- * Windows
62
- * MacOS X
63
- * Linux
61
+ * Windows (architectures: x64, x86)
62
+ * MacOS X (architectures: x86_64, aarch64)
63
+ * Linux (architectures: x86_64, x86, aarch64)
64
64
 
65
65
  with Python versions:
66
66
 
67
- * 3.7
68
67
  * 3.8
69
68
  * 3.9
70
69
  * 3.10
71
70
  * 3.11
72
-
73
- and architectures:
74
-
75
- * x84_64
76
- * arm64 (known as M1/Apple Chip - MacOS only)
71
+ * 3.12
77
72
 
78
73
  **Requirements for installation from source**
79
74
 
@@ -34,22 +34,17 @@ Installation
34
34
 
35
35
  Wheels are available for:
36
36
 
37
- * Windows
38
- * MacOS X
39
- * Linux
37
+ * Windows (architectures: x64, x86)
38
+ * MacOS X (architectures: x86_64, aarch64)
39
+ * Linux (architectures: x86_64, x86, aarch64)
40
40
 
41
41
  with Python versions:
42
42
 
43
- * 3.7
44
43
  * 3.8
45
44
  * 3.9
46
45
  * 3.10
47
46
  * 3.11
48
-
49
- and architectures:
50
-
51
- * x84_64
52
- * arm64 (known as M1/Apple Chip - MacOS only)
47
+ * 3.12
53
48
 
54
49
  **Requirements for installation from source**
55
50
 
@@ -1,4 +1,4 @@
1
- portforward-go
1
+ portforward
2
2
  ==============
3
3
 
4
4
  .. toctree::
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "portforward"
7
- version = "0.6.1"
7
+ version = "0.7.0"
8
8
  authors = [{ name = "Sebastian Ziemann", email = "corka149@mailbox.org" }]
9
9
  description = "Easy Kubernetes Port-Forward For Python"
10
10
  readme = "README.rst"
@@ -34,7 +34,7 @@ module-name = "portforward._portforward"
34
34
  python-source = "python"
35
35
 
36
36
  [tool.bumpversion]
37
- current_version = "0.6.1"
37
+ current_version = "0.7.0"
38
38
  tag = true
39
39
  commit = true
40
40
 
@@ -2,14 +2,15 @@
2
2
  Easy Kubernetes Port-Forward For Python
3
3
  """
4
4
 
5
- __version__ = "0.6.1"
5
+ __version__ = "0.7.0"
6
6
 
7
7
  import asyncio
8
8
  import contextlib
9
+ import ipaddress
9
10
  import os
10
11
  from enum import Enum
11
12
  from pathlib import Path
12
- from typing import Generator, Optional
13
+ from typing import Generator, Optional, Union
13
14
 
14
15
  from portforward import _portforward
15
16
 
@@ -36,6 +37,7 @@ def forward(
36
37
  waiting: float = 0.1,
37
38
  log_level: LogLevel = LogLevel.INFO,
38
39
  kube_context: str = "",
40
+ bind_ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str, None] = None,
39
41
  ) -> Generator["PortForwarder", None, None]:
40
42
  """
41
43
  Connects to a **pod or service** and tunnels traffic from a local port to
@@ -52,21 +54,16 @@ def forward(
52
54
  >>> import portforward
53
55
  >>> with portforward.forward("test", "web-svc", 9000, 80):
54
56
  >>> # Do work
55
- >>>
56
- >>> # Or without context manager
57
- >>>
58
- >>> forwarder = portforward.forward("test", "some-pod", 9000, 80)
59
- >>> # Do work
60
- >>> forwarder.stop()
61
57
 
62
58
  :param namespace: Target namespace
63
59
  :param pod_or_service: Name of target Pod or service
64
- :param from_port: Local port
60
+ :param from_port: Local port, or 0 to use any free port
65
61
  :param to_port: Port inside the pod
66
62
  :param config_path: Path for loading kube config
67
63
  :param waiting: Delay in seconds
68
64
  :param log_level: Level of logging
69
65
  :param kube_context: Target kubernetes context (fallback is current context)
66
+ :param bind_ip: To which IP shall the portforward be bind
70
67
  :return: forwarder to manual stop the forwarding
71
68
  """
72
69
 
@@ -79,6 +76,7 @@ def forward(
79
76
  waiting,
80
77
  log_level,
81
78
  kube_context,
79
+ bind_ip,
82
80
  )
83
81
 
84
82
  try:
@@ -107,6 +105,7 @@ class PortForwarder:
107
105
  waiting: float = 0.1,
108
106
  log_level: LogLevel = LogLevel.INFO,
109
107
  kube_context: str = "",
108
+ bind_ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str, None] = None,
110
109
  ) -> None:
111
110
  self._async_forwarder = AsyncPortForwarder(
112
111
  namespace,
@@ -117,6 +116,7 @@ class PortForwarder:
117
116
  waiting,
118
117
  log_level,
119
118
  kube_context,
119
+ bind_ip,
120
120
  )
121
121
 
122
122
  def forward(self):
@@ -129,6 +129,11 @@ class PortForwarder:
129
129
  def is_stopped(self):
130
130
  return self._async_forwarder.is_stopped
131
131
 
132
+ @property
133
+ def from_port(self):
134
+ """The local port that was actually used for the portforward."""
135
+ return self._async_forwarder.from_port
136
+
132
137
 
133
138
  class AsyncPortForwarder:
134
139
  """Use the same args as the `portforward.forward` method."""
@@ -143,10 +148,10 @@ class AsyncPortForwarder:
143
148
  waiting: float = 0.1,
144
149
  log_level: LogLevel = LogLevel.INFO,
145
150
  kube_context: str = "",
151
+ bind_ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str, None] = None,
146
152
  ) -> None:
147
153
  self.namespace: str = _validate_str("namespace", namespace)
148
154
  self.pod_or_service: str = _validate_str("pod_or_service", pod_or_service)
149
- self.from_port: int = _validate_port("from_port", from_port)
150
155
  self.to_port: int = _validate_port("to_port", to_port)
151
156
  self.log_level: LogLevel = _validate_log(log_level)
152
157
  self.waiting: float = waiting
@@ -154,14 +159,19 @@ class AsyncPortForwarder:
154
159
  self.config_path: str = _config_path(config_path)
155
160
  self.kube_context: str = _kube_context(kube_context)
156
161
 
162
+ _validate_port("from_port", from_port)
163
+ bind_ip = _validate_ip_address(bind_ip)
164
+
157
165
  self.actual_pod_name: str = ""
166
+ self.from_port: int = 0
158
167
  self._is_stopped: bool = False
168
+ self.bind_address: str = f"{bind_ip}:{from_port}"
159
169
 
160
170
  async def forward(self):
161
- self.actual_pod_name = await _portforward.forward(
171
+ (self.actual_pod_name, self.from_port) = await _portforward.forward(
162
172
  self.namespace,
163
173
  self.pod_or_service,
164
- self.from_port,
174
+ self.bind_address,
165
175
  self.to_port,
166
176
  self.config_path,
167
177
  self.log_level.value,
@@ -196,7 +206,7 @@ def _validate_str(arg_name, arg) -> str:
196
206
 
197
207
 
198
208
  def _validate_port(arg_name, arg) -> int:
199
- in_range = arg and 0 < arg < 65536
209
+ in_range = arg is not None and 0 <= arg < 65536
200
210
  if arg is None or not isinstance(arg, int) or not in_range:
201
211
  raise ValueError(f"{arg_name}={arg} is not a valid port")
202
212
 
@@ -210,6 +220,18 @@ def _validate_log(log_level):
210
220
  return log_level
211
221
 
212
222
 
223
+ def _validate_ip_address(ip_address):
224
+ if not ip_address:
225
+ return "127.0.0.1"
226
+
227
+ if isinstance(ip_address, ipaddress.IPv4Address) or isinstance(
228
+ ip_address, ipaddress.IPv4Address
229
+ ):
230
+ return str(ip_address)
231
+
232
+ return str(ipaddress.ip_address(ip_address))
233
+
234
+
213
235
  def _config_path(config_path_arg) -> str:
214
236
  if config_path_arg and not isinstance(config_path_arg, str):
215
237
  raise ValueError(f"config_path={config_path_arg} is not a valid str")
@@ -5,7 +5,7 @@ Rust native module / Python C Extension
5
5
  async def forward(
6
6
  namespace: str,
7
7
  pod_or_service: str,
8
- from_port: int,
8
+ bind_address: str,
9
9
  to_port: int,
10
10
  config_path: str,
11
11
  log_level: int,
@@ -9,7 +9,7 @@ fn forward(
9
9
  py: Python<'_>,
10
10
  namespace: String,
11
11
  pod_or_service: String,
12
- from_port: u16,
12
+ bind_address: String,
13
13
  to_port: u16,
14
14
  config_path: String,
15
15
  log_level: u64,
@@ -20,7 +20,7 @@ fn forward(
20
20
  let config = portforward::ForwardConfig::builder()
21
21
  .namespace(namespace)
22
22
  .pod_or_service(pod_or_service)
23
- .from_port(from_port)
23
+ .bind_address(bind_address)
24
24
  .to_port(to_port)
25
25
  .config_path(config_path)
26
26
  .kube_context(kube_context)
@@ -14,6 +14,7 @@ use log::*;
14
14
  use once_cell::sync::Lazy;
15
15
  use std::net::SocketAddr;
16
16
  use std::{collections::HashMap, path::Path};
17
+ use std::str::FromStr;
17
18
  use tokio::{
18
19
  io::{AsyncRead, AsyncWrite},
19
20
  net::TcpListener,
@@ -27,15 +28,16 @@ use typed_builder::TypedBuilder;
27
28
  pub struct ForwardConfig {
28
29
  namespace: String,
29
30
  pod_or_service: String,
30
- from_port: u16,
31
+ bind_address: String,
31
32
  to_port: u16,
32
33
  config_path: String,
33
34
  kube_context: String,
34
35
  }
35
36
 
36
- /// Creates a connection to a pod. It returns the actual pod name for the portforward.
37
+ /// Creates a connection to a pod. It returns a `(pod_name, from_port)` tuple
38
+ /// with the actual pod name and local port used for the portforward.
37
39
  /// It differs from `pod_or_service` when `pod_or_service` represents a service.
38
- pub async fn forward(config: ForwardConfig) -> anyhow::Result<String> {
40
+ pub async fn forward(config: ForwardConfig) -> anyhow::Result<(String, u16)> {
39
41
  debug!("{:?}", config);
40
42
 
41
43
  let client_config = load_config(&config.config_path, &config.kube_context).await?;
@@ -55,8 +57,9 @@ pub async fn forward(config: ForwardConfig) -> anyhow::Result<String> {
55
57
 
56
58
  PORTFORWARD_REGISTRY.register(&q_name, forwarding).await;
57
59
 
58
- let addr = SocketAddr::from(([127, 0, 0, 1], config.from_port));
60
+ let addr = SocketAddr::from_str(&config.bind_address).with_context(move || config.bind_address)?;
59
61
  let tcp_listener = TcpListener::bind(addr).await?;
62
+ let from_port = tcp_listener.local_addr()?.port();
60
63
  let forward_task = setup_forward_task(
61
64
  tcp_listener,
62
65
  rx,
@@ -67,7 +70,7 @@ pub async fn forward(config: ForwardConfig) -> anyhow::Result<String> {
67
70
 
68
71
  tokio::spawn(forward_task);
69
72
 
70
- return Ok(q_name.pod_name);
73
+ return Ok((q_name.pod_name, from_port));
71
74
  }
72
75
 
73
76
  async fn load_config(
@@ -80,7 +83,7 @@ async fn load_config(
80
83
  return Ok(incluster_config);
81
84
  }
82
85
 
83
- let kube_config = kube::config::Kubeconfig::read_from(config_path.clone())?;
86
+ let kube_config = kube::config::Kubeconfig::read_from(config_path)?;
84
87
  let mut options = kube::config::KubeConfigOptions::default();
85
88
 
86
89
  // "" is the sign for using default context
@@ -124,6 +124,33 @@ def test_service_portforward_with_success(kind_cluster: KindCluster):
124
124
  response: requests.Response = requests.get(url_2)
125
125
  pytest.fail("Portforward should be closed after leaving the context manager")
126
126
 
127
+ def test_portforward_from_port_zero_assigns_port(kind_cluster: KindCluster):
128
+ # Arrange
129
+ _create_test_resources(kind_cluster)
130
+
131
+ pod_name = "test-pod"
132
+ config = str(kind_cluster.kubeconfig_path.absolute())
133
+
134
+ local_port = 0 # from port
135
+ pod_port = 3000 # to port
136
+
137
+ pf = portforward.forward(
138
+ TEST_NAMESPACE,
139
+ pod_name,
140
+ local_port,
141
+ pod_port,
142
+ config_path=config,
143
+ kube_context=TEST_CONTEXT,
144
+ )
145
+
146
+ # Act & Assert
147
+ with pf as forwarder:
148
+ assert not forwarder.is_stopped()
149
+ assert forwarder.from_port != 0
150
+ url = f"http://localhost:{forwarder.from_port}/ping"
151
+ response: requests.Response = requests.get(url)
152
+ assert response.status_code == 200
153
+
127
154
 
128
155
  @pytest.mark.parametrize(
129
156
  "namespace,pod,from_port,to_port",
@@ -160,11 +187,23 @@ def test_forward_invalid_parameter(namespace, pod, from_port, to_port):
160
187
  pytest.fail("Should raise error before")
161
188
 
162
189
 
190
+ def test_validate_ip_address():
191
+ namespace = "test_ns"
192
+ pod = "test_pod"
193
+ from_port = 9000
194
+ to_port = 10000
195
+ bind_ip = "not-an-ip-adress"
196
+
197
+ with pytest.raises(ValueError):
198
+ with portforward.forward(namespace, pod, from_port, to_port, bind_ip=bind_ip):
199
+ pytest.fail("Should raise error before")
200
+
201
+
163
202
  def test_forward_raise_error():
164
203
  """Tests the conversion of the C extension error into the Python Error"""
165
204
 
166
205
  # Arrange
167
- namespace = "test" + str(uuid.uuid4()) # Should never exists
206
+ namespace = "test" + str(uuid.uuid4()) # Should never exist
168
207
  pod = "web"
169
208
  from_ = 9000
170
209
  to = 80
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes