Skip to content

feat: add extraPortMappings support to ClusterSpec - #14

Closed
Ladas wants to merge 3 commits into
praxis-proxy:mainfrom
Ladas:feat/extra-port-mappings
Closed

feat: add extraPortMappings support to ClusterSpec#14
Ladas wants to merge 3 commits into
praxis-proxy:mainfrom
Ladas:feat/extra-port-mappings

Conversation

@Ladas

@Ladas Ladas commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • Expose KIND extraPortMappings in the forge config schema via a new ports field on ClusterSpec
  • Enables mapping host ports to container/NodePort ports for accessing services (Grafana, Prometheus, MLflow) from the host
  • Includes config validation (unique host ports, valid port ranges), KIND config generation, and integration test

Context

Split out from the closed #2 (OTel benchmark demo). The demo content moves to praxis-proxy/experimental, but this forge CLI feature is needed independently — any forge environment that exposes NodePort services to the host needs it.

Test plan

  • Integration test with tests/fixtures/port-mappings.yaml
  • Validates duplicate host ports are rejected
  • Validates port range (1-65535)
  • KIND config correctly generates extraPortMappings entries

@Ladas
Ladas force-pushed the feat/extra-port-mappings branch from 54fd771 to 2ee643c Compare August 26, 2026 09:58
Ladas added a commit to Ladas/experimental that referenced this pull request Aug 26, 2026
Full observability stack on KIND for benchmarking Praxis proxy OTel
tracing overhead. Deploys Prometheus, Grafana, Tempo, Loki, OTel
Collector, MLflow, Fortio echo, and llm-d inference-sim.

Two benchmark scenarios:
- Core proxy (2000 RPS, 10 spans/request, GET to echo)
- AI proxy (500 RPS, 11 spans/request, POST /v1/chat/completions)

Each runs baseline/otel-noop/otel-full configurations with vegeta.

Includes 5 Grafana dashboards, benchmark scripts with automated
report generation, and per-scenario config isolation.

Requires praxis-forge with extraPortMappings support
(praxis-proxy/forge#14).

Signed-off-by: Ladislav Smola <lsmola@redhat.com>

@praxis-bot praxis-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat: add extraPortMappings support to ClusterSpec

Clean feature addition overall. The CreateClusterConfig struct is a good refactor over the growing parameter list, validation is thorough (port ranges, protocol, bind address, cross-scope conflicts), and test coverage is solid. Two medium findings below.

Comment thread src/config/validate.rs Outdated
}
check_port_bind_address(&pm.bind_address, &ctx)?;
check_cluster_port_protocol(&pm.protocol, &ctx)?;
let key = (pm.host, pm.protocol.to_lowercase());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium] Intra-cluster duplicate detection ignores bind_address, inconsistent with check_cross_scope_port_conflicts which includes it in the key.

With the current key (pm.host, pm.protocol), two port mappings on different bind addresses within the same cluster (e.g. 127.0.0.1:8080/tcp and 10.0.0.1:8080/tcp) are rejected as duplicates even though they do not conflict. Meanwhile check_port_collision (used by check_cross_scope_port_conflicts) correctly allows this by including bind_address in the key.

Include bind_address in the intra-cluster duplicate key to match:

let key = (
    pm.bind_address.clone().unwrap_or_default(),
    pm.host,
    pm.protocol.to_lowercase(),
);

Comment thread src/cluster/kind.rs
#[test]
fn generate_kind_config_with_port_mappings() {
let nodes = NodeConfig::default();
let ports = vec![

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium] The listenAddress generation branch (line 160-162) is not covered by any unit test. Both port mappings here use bind_address: None, so the if let Some(addr) = &pm.bind_address path is never exercised.

Add a port with a bind_address to this test (or a dedicated test) and assert the generated YAML contains the expected listenAddress entry:

PortMapping {
    bind_address: Some("127.0.0.1".to_owned()),
    host: 9090,
    container: 30909,
    protocol: "tcp".to_owned(),
},

then:

assert!(
    yaml.contains("listenAddress: \"127.0.0.1\""),
    "should include listenAddress for bind_address",
);

@Ladas
Ladas force-pushed the feat/extra-port-mappings branch from 2ee643c to 1f28043 Compare August 27, 2026 15:51
Ladas added a commit to Ladas/experimental that referenced this pull request Aug 27, 2026
Full observability stack on KIND for benchmarking Praxis experimental
server OTel tracing overhead across two scenarios:
- Core proxy (2000 RPS, 10 spans/request, GET to echo)
- AI proxy (500 RPS, 11 spans/request, POST /v1/chat/completions)

Each runs baseline/otel-noop/otel-full configurations with vegeta.

Adds otel feature to praxis-experimental-server (forwarding
praxis-core/otel + praxis-filter/otel), FEATURES build-arg to
Containerfile, and patches praxis crates to main (1b439271) for
the Tokio runtime fix and filter/otel feature not yet in v0.5.3.

Stack: Prometheus, Grafana 11.x, Tempo, Loki, OTel Collector,
MLflow, Fortio echo, llm-d inference-sim.

Includes 5 Grafana dashboards, benchmark scripts with automated
report generation, and per-scenario config isolation.

Requires praxis-forge with extraPortMappings (praxis-proxy/forge#14).

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
Ladas added a commit to Ladas/experimental that referenced this pull request Aug 27, 2026
Full observability stack on KIND for benchmarking Praxis experimental
server OTel tracing overhead across two scenarios:
- Core proxy (2000 RPS, 10 spans/request, GET to echo)
- AI proxy (500 RPS, 11 spans/request, POST /v1/chat/completions)

Each runs baseline/otel-noop/otel-full configurations with vegeta.

Adds otel feature to praxis-experimental-server (forwarding
praxis-core/otel + praxis-filter/otel), FEATURES build-arg to
Containerfile, and patches praxis crates to main (1b439271) for
the Tokio runtime fix and filter/otel feature not yet in v0.5.3.

Stack: Prometheus, Grafana 11.x, Tempo, Loki, OTel Collector,
MLflow, Fortio echo, llm-d inference-sim.

Includes 5 Grafana dashboards, benchmark scripts with automated
report generation, and per-scenario config isolation.

Requires praxis-forge with extraPortMappings (praxis-proxy/forge#14).

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
Ladas added a commit to Ladas/experimental that referenced this pull request Aug 27, 2026
Full observability stack on KIND for benchmarking Praxis experimental
server OTel tracing overhead across two scenarios:

- AI gateway (500 RPS, 11 spans/request, POST /v1/chat/completions)
- Core proxy (2000 RPS, 10 spans/request, GET to echo backend)

Each scenario runs baseline/otel-noop/otel-full configurations with
vegeta load testing via a single parameterized benchmark.sh script.

Stack: Prometheus, Grafana 11.x, Tempo, Loki, OTel Collector,
MLflow, Fortio echo, llm-d inference-sim, plus 5 Grafana dashboards.

Also adds:
- otel feature to praxis-experimental-server (praxis-core/otel +
  praxis-filter/otel) with FEATURES build-arg in Containerfile
- Patches praxis crates to main rev 1b439271 for Tokio runtime fix
  and filter/otel feature not yet in v0.5.3
- praxis-main feature on AI dep for log_level field compatibility

Requires praxis-forge with extraPortMappings (praxis-proxy/forge#14).

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
Ladas added a commit to Ladas/experimental that referenced this pull request Aug 28, 2026
Full observability stack on KIND for benchmarking Praxis experimental
server OTel tracing overhead across two scenarios:

- AI gateway (500 RPS, 11 spans/request, POST /v1/chat/completions)
- Core proxy (2000 RPS, 10 spans/request, GET to echo backend)

Each scenario runs baseline/otel-noop/otel-full configurations with
vegeta load testing via a single parameterized benchmark.sh script.

Stack: Prometheus, Grafana 11.x, Tempo, Loki, OTel Collector,
MLflow, Fortio echo, llm-d inference-sim, plus 5 Grafana dashboards.

Also adds:
- otel feature to praxis-experimental-server (praxis-core/otel +
  praxis-filter/otel) with FEATURES build-arg in Containerfile
- Patches praxis crates to main rev 1b439271 for Tokio runtime fix
  and filter/otel feature not yet in v0.5.3
- praxis-main feature on AI dep for log_level field compatibility

Requires praxis-forge with extraPortMappings (praxis-proxy/forge#14).

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
Ladas added 3 commits August 28, 2026 07:55
Expose KIND extraPortMappings in the forge config schema so clusters
can map host ports to container/NodePort ports. This enables accessing
services like Grafana, Prometheus, and MLflow from the host.

Includes config validation (unique host ports, valid ranges), KIND
config generation, and integration test with a port-mappings fixture.

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
- Validate bind_address as a valid IP for cluster ports (parity with services)
- Restrict protocol to tcp/udp for cluster ports
- Check for host-port collisions across clusters and services
- Extend test fixture with bindAddress and protocol: udp

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
The cross-scope collision check already uses (bind_address, host, protocol)
but the intra-cluster check only used (host, protocol), rejecting valid
configs with same host port on different bind addresses within one cluster.

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
@Ladas
Ladas force-pushed the feat/extra-port-mappings branch from 1f28043 to fdb4ef7 Compare August 28, 2026 05:55
Ladas added a commit to Ladas/experimental that referenced this pull request Aug 28, 2026
Full observability stack on KIND for benchmarking Praxis experimental
server OTel tracing overhead across two scenarios:

- AI gateway (500 RPS, 11 spans/request, POST /v1/chat/completions)
- Core proxy (2000 RPS, 10 spans/request, GET to echo backend)

Each scenario runs baseline/otel-noop/otel-full configurations with
vegeta load testing via a single parameterized benchmark.sh script.

Stack: Prometheus, Grafana 11.x, Tempo, Loki, OTel Collector,
MLflow, Fortio echo, llm-d inference-sim, plus 5 Grafana dashboards.

Also adds:
- otel feature to praxis-experimental-server (praxis-core/otel +
  praxis-filter/otel) with FEATURES build-arg in Containerfile
- Patches praxis crates to main rev 1b439271 for Tokio runtime fix
  and filter/otel feature not yet in v0.5.3
- praxis-main feature on AI dep for log_level field compatibility

Requires praxis-forge with extraPortMappings (praxis-proxy/forge#14).

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
Ladas added a commit to Ladas/experimental that referenced this pull request Aug 28, 2026
Full observability stack on KIND for benchmarking Praxis experimental
server OTel tracing overhead across two scenarios:

- AI gateway (500 RPS, 11 spans/request, POST /v1/chat/completions)
- Core proxy (2000 RPS, 10 spans/request, GET to echo backend)

Each scenario runs baseline/otel-noop/otel-full configurations with
vegeta load testing via a single parameterized benchmark.sh script.

Stack: Prometheus, Grafana 11.x, Tempo, Loki, OTel Collector,
MLflow, Fortio echo, llm-d inference-sim, plus 5 Grafana dashboards.

Also adds:
- otel feature to praxis-experimental-server (praxis-core/otel +
  praxis-filter/otel) with FEATURES build-arg in Containerfile
- Patches praxis crates to main rev 1b439271 for Tokio runtime fix
  and filter/otel feature not yet in v0.5.3
- praxis-main feature on AI dep for log_level field compatibility

Requires praxis-forge with extraPortMappings (praxis-proxy/forge#14).

Signed-off-by: Ladislav Smola <lsmola@redhat.com>
@Ladas Ladas closed this Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants