Skip to content

Commit

Permalink
Merge pull request #39 from corka149/38-bind-to-0000
Browse files Browse the repository at this point in the history
Allow binding to different ip
  • Loading branch information
corka149 authored Jun 19, 2024
2 parents bad1726 + 9e0dacd commit 4b26c3f
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 10 deletions.
8 changes: 8 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
History
=======

0.6.2 (2024-06-19)
------------------
* Allow defining binding ip

0.6.1 (2024-01-25)
------------------
* Fixed wrong logger setup

0.6.0 (2023-06-13)
------------------
* Rewrite native part in Rust with support of Py03 and maturin
Expand Down
28 changes: 25 additions & 3 deletions python/portforward/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

import asyncio
import contextlib
import ipaddress
import os
from enum import Enum
from pathlib import Path
from typing import Generator, Optional
from typing import Generator, Optional, Union

from portforward import _portforward

Expand All @@ -36,6 +37,7 @@ def forward(
waiting: float = 0.1,
log_level: LogLevel = LogLevel.INFO,
kube_context: str = "",
bind_ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str, None] = None,
) -> Generator["PortForwarder", None, None]:
"""
Connects to a **pod or service** and tunnels traffic from a local port to
Expand All @@ -61,6 +63,7 @@ def forward(
:param waiting: Delay in seconds
:param log_level: Level of logging
:param kube_context: Target kubernetes context (fallback is current context)
:param bind_ip: To which IP shall the portforward be bind
:return: forwarder to manual stop the forwarding
"""

Expand All @@ -73,6 +76,7 @@ def forward(
waiting,
log_level,
kube_context,
bind_ip,
)

try:
Expand Down Expand Up @@ -101,6 +105,7 @@ def __init__(
waiting: float = 0.1,
log_level: LogLevel = LogLevel.INFO,
kube_context: str = "",
bind_ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str, None] = None,
) -> None:
self._async_forwarder = AsyncPortForwarder(
namespace,
Expand All @@ -111,6 +116,7 @@ def __init__(
waiting,
log_level,
kube_context,
bind_ip,
)

def forward(self):
Expand All @@ -137,25 +143,29 @@ def __init__(
waiting: float = 0.1,
log_level: LogLevel = LogLevel.INFO,
kube_context: str = "",
bind_ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str, None] = None,
) -> None:
self.namespace: str = _validate_str("namespace", namespace)
self.pod_or_service: str = _validate_str("pod_or_service", pod_or_service)
self.from_port: int = _validate_port("from_port", from_port)
self.to_port: int = _validate_port("to_port", to_port)
self.log_level: LogLevel = _validate_log(log_level)
self.waiting: float = waiting

self.config_path: str = _config_path(config_path)
self.kube_context: str = _kube_context(kube_context)

_validate_port("from_port", from_port)
bind_ip = _validate_ip_address(bind_ip)

self.actual_pod_name: str = ""
self._is_stopped: bool = False
self.bind_address: str = f"{bind_ip}:{from_port}"

async def forward(self):
self.actual_pod_name = await _portforward.forward(
self.namespace,
self.pod_or_service,
self.from_port,
self.bind_address,
self.to_port,
self.config_path,
self.log_level.value,
Expand Down Expand Up @@ -204,6 +214,18 @@ def _validate_log(log_level):
return log_level


def _validate_ip_address(ip_address):
if not ip_address:
return "127.0.0.1"

if isinstance(ip_address, ipaddress.IPv4Address) or isinstance(
ip_address, ipaddress.IPv4Address
):
return str(ip_address)

return str(ipaddress.ip_address(ip_address))


def _config_path(config_path_arg) -> str:
if config_path_arg and not isinstance(config_path_arg, str):
raise ValueError(f"config_path={config_path_arg} is not a valid str")
Expand Down
2 changes: 1 addition & 1 deletion python/portforward/_portforward.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Rust native module / Python C Extension
async def forward(
namespace: str,
pod_or_service: str,
from_port: int,
bind_address: str,
to_port: int,
config_path: str,
log_level: int,
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn forward(
py: Python<'_>,
namespace: String,
pod_or_service: String,
from_port: u16,
bind_address: String,
to_port: u16,
config_path: String,
log_level: u64,
Expand All @@ -20,7 +20,7 @@ fn forward(
let config = portforward::ForwardConfig::builder()
.namespace(namespace)
.pod_or_service(pod_or_service)
.from_port(from_port)
.bind_address(bind_address)
.to_port(to_port)
.config_path(config_path)
.kube_context(kube_context)
Expand Down
7 changes: 4 additions & 3 deletions src/portforward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use log::*;
use once_cell::sync::Lazy;
use std::net::SocketAddr;
use std::{collections::HashMap, path::Path};
use std::str::FromStr;
use tokio::{
io::{AsyncRead, AsyncWrite},
net::TcpListener,
Expand All @@ -27,7 +28,7 @@ use typed_builder::TypedBuilder;
pub struct ForwardConfig {
namespace: String,
pod_or_service: String,
from_port: u16,
bind_address: String,
to_port: u16,
config_path: String,
kube_context: String,
Expand Down Expand Up @@ -55,7 +56,7 @@ pub async fn forward(config: ForwardConfig) -> anyhow::Result<String> {

PORTFORWARD_REGISTRY.register(&q_name, forwarding).await;

let addr = SocketAddr::from(([127, 0, 0, 1], config.from_port));
let addr = SocketAddr::from_str(&config.bind_address).with_context(move || config.bind_address)?;
let tcp_listener = TcpListener::bind(addr).await?;
let forward_task = setup_forward_task(
tcp_listener,
Expand All @@ -80,7 +81,7 @@ async fn load_config(
return Ok(incluster_config);
}

let kube_config = kube::config::Kubeconfig::read_from(config_path.clone())?;
let kube_config = kube::config::Kubeconfig::read_from(config_path)?;
let mut options = kube::config::KubeConfigOptions::default();

// "" is the sign for using default context
Expand Down
14 changes: 13 additions & 1 deletion tests/test_portforward.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,23 @@ def test_forward_invalid_parameter(namespace, pod, from_port, to_port):
pytest.fail("Should raise error before")


def test_validate_ip_address():
namespace = "test_ns"
pod = "test_pod"
from_port = 9000
to_port = 10000
bind_ip = "not-an-ip-adress"

with pytest.raises(ValueError):
with portforward.forward(namespace, pod, from_port, to_port, bind_ip=bind_ip):
pytest.fail("Should raise error before")


def test_forward_raise_error():
"""Tests the conversion of the C extension error into the Python Error"""

# Arrange
namespace = "test" + str(uuid.uuid4()) # Should never exists
namespace = "test" + str(uuid.uuid4()) # Should never exist
pod = "web"
from_ = 9000
to = 80
Expand Down

0 comments on commit 4b26c3f

Please sign in to comment.