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.
- portforward-0.7.0/.github/workflows/release.yml +120 -0
- {portforward-0.6.1 → portforward-0.7.0}/.readthedocs.yml +5 -1
- {portforward-0.6.1 → portforward-0.7.0}/Cargo.lock +1 -1
- {portforward-0.6.1 → portforward-0.7.0}/Cargo.toml +1 -1
- {portforward-0.6.1 → portforward-0.7.0}/HISTORY.rst +12 -0
- {portforward-0.6.1 → portforward-0.7.0}/Makefile +4 -4
- {portforward-0.6.1 → portforward-0.7.0}/PKG-INFO +6 -11
- {portforward-0.6.1 → portforward-0.7.0}/README.rst +4 -9
- {portforward-0.6.1 → portforward-0.7.0}/docs/modules.rst +1 -1
- {portforward-0.6.1 → portforward-0.7.0}/pyproject.toml +2 -2
- {portforward-0.6.1 → portforward-0.7.0}/python/portforward/__init__.py +35 -13
- {portforward-0.6.1 → portforward-0.7.0}/python/portforward/_portforward.pyi +1 -1
- {portforward-0.6.1 → portforward-0.7.0}/src/lib.rs +2 -2
- {portforward-0.6.1 → portforward-0.7.0}/src/portforward.rs +9 -6
- {portforward-0.6.1 → portforward-0.7.0}/tests/test_portforward.py +40 -1
- {portforward-0.6.1 → portforward-0.7.0}/.editorconfig +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/.github/ISSUE_TEMPLATE.md +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/.github/workflows/python-app.yml +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/.gitignore +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/AUTHORS.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/CONTRIBUTING.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/LICENSE +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/Makefile +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/authors.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/conf.py +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/contributing.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/docs/conf.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/docs/modules.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/history.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/index.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/installation.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/make.bat +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/docs/portforward.rst +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/python/portforward/py.typed +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/requirements-dev.txt +0 -0
- {portforward-0.6.1 → portforward-0.7.0}/tests/conftest.py +0 -0
- {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
|
|
@@ -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.
|
|
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
|
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "portforward"
|
|
7
|
-
version = "0.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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")
|
|
@@ -9,7 +9,7 @@ fn forward(
|
|
|
9
9
|
py: Python<'_>,
|
|
10
10
|
namespace: String,
|
|
11
11
|
pod_or_service: String,
|
|
12
|
-
|
|
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
|
-
.
|
|
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
|
-
|
|
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
|
|
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::
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|