Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds repeated Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@appconfig/appconfig.go`:
- Around line 243-257: Duplicate detection for ports uses the raw p.Protocol
value, so an empty protocol and "tcp" are treated as different even though empty
should default to "tcp"; update the normalization before duplicate checks by
canonicalizing p.Protocol (e.g., set protocol := p.Protocol; if protocol=="" {
protocol = "tcp" }) and use that normalized value when constructing pp :=
portProto{p.Port, protocol} and when formatting error messages; ensure
seenPortProto and duplicate checks use the normalized protocol while seenNames
logic remains unchanged and still references p.Name and serviceName.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
api/core/config_version.goapi/core/core_v1alpha/schema.gen.goapi/core/schema.ymlappconfig/appconfig.goappconfig/appconfig_test.gocontrollers/deployment/launcher.gocontrollers/deployment/launcher_test.goservers/build/build.goservers/build/build_test.go
evanphx
left a comment
There was a problem hiding this comment.
This all looks good, but have it work, a network_v1alpha.Service has to be created but I don't see it here. Something like this: https://github.com/mirendev/runtime/blob/main/pkg/addon/framework.go#L139
|
lol you're right - all this config wiring missed the last mile! fixing |
2dcdcbc to
14d97b4
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
testdata/tcp-echo/main.go (1)
13-13: Trim redundant inline comments.At Line 13 and Line 33, the comments restate what the code already clearly expresses. Removing them would keep the sample tighter.
Proposed cleanup
- // TCP echo server on port 7000 go func() { @@ - // HTTP health endpoint on port 3000 port := os.Getenv("PORT")As per coding guidelines, "Only add comments when they provide valuable context or explain 'why' something is done - avoid redundant comments that restate what the code does".
Also applies to: 33-33
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@testdata/tcp-echo/main.go` at line 13, Remove the redundant inline comments that merely restate the code: delete the comment on the TCP port declaration and the comment inside the echo handler so the sample stays concise; locate the comments near the top of the file around the TCP port setup (in main or any net.Listen call) and the comment inside the echo loop/handler (within func main or the goroutine handling connections) and remove them, leaving only comments that add non-obvious context.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@appconfig/appconfig.go`:
- Around line 246-252: Validation currently accepts "udp" but the dataplane only
supports TCP; update appconfig validation to reject "udp" (treat any "udp"
port.protocol as invalid) so configs cannot declare unsupported protocols. In
appconfig.go change the checks around p.Protocol (the block setting proto :=
p.Protocol and the similar block at the other occurrence) to return an error
when p.Protocol == "udp" (allow only "" or "tcp"), and keep the default proto
logic as "tcp" for empty values; reference the existing symbols p.Protocol and
proto so the change aligns with the other occurrence at the second block.
- Around line 229-230: The mixed-style detection currently checks svcConfig.Port
> 0 which misses negative scalar port values; update the condition in the
validation that returns the error for mixed 'ports' array and scalar fields so
it uses svcConfig.Port != 0 instead of > 0, keeping the existing checks for
svcConfig.PortName != "" and svcConfig.PortType != "" (this is the block that
constructs the error message using serviceName), so any non-zero scalar port
(including negatives) triggers the mixed-style error.
In `@controllers/deployment/launcher.go`:
- Around line 237-253: The current cleanup can delete services when
ensureServiceForPorts transiently fails because desiredServices only contains
names from successful ensures; change the loop that builds desiredServices so it
collects svc.Name for every spec.Services entry regardless of
ensureServiceForPorts outcome (i.e., append svc.Name before or unconditionally
after calling ensureServiceForPorts), keep the existing error logging and
continue on error, and then call cleanupStaleServices with that full list so
cleanup does not remove active Service entities on partial reconcile failures;
reference ensureServiceForPorts, desiredServices, svc.Name, and
cleanupStaleServices to locate the change.
- Around line 1039-1059: The function ensureServiceForPorts currently only
checks svc.Ports (core_v1alpha.ConfigSpecServicesPorts) and misses scalar
fields; update ensureServiceForPorts to treat scalar svc.Port and svc.PortType
as a fallback (or normalize them into svc.Ports at start) so non-HTTP scalar
ports trigger Service creation: when iterating spec.Services in
ensureServiceForPorts, if svc.Ports is empty but svc.Port or svc.PortType is
set, create a Ports entry from those scalar fields (or treat svc.PortType !=
"http" as hasNonHTTP) before the hasNonHTTP check; reference
ensureServiceForPorts, core_v1alpha.ConfigSpecServicesPorts, svc.Ports,
svc.Port, svc.PortType and ConfigSpecFromConfig in your change to keep behavior
consistent and add unit tests covering scalar non-HTTP ports.
---
Nitpick comments:
In `@testdata/tcp-echo/main.go`:
- Line 13: Remove the redundant inline comments that merely restate the code:
delete the comment on the TCP port declaration and the comment inside the echo
handler so the sample stays concise; locate the comments near the top of the
file around the TCP port setup (in main or any net.Listen call) and the comment
inside the echo loop/handler (within func main or the goroutine handling
connections) and remove them, leaving only comments that add non-obvious
context.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (14)
api/core/config_version.goapi/core/core_v1alpha/schema.gen.goapi/core/schema.ymlappconfig/appconfig.goappconfig/appconfig_test.gocontrollers/deployment/launcher.gocontrollers/deployment/launcher_test.gocontrollers/service/service.gocontrollers/service/service_test.goservers/build/build.goservers/build/build_test.gotestdata/tcp-echo/.miren/app.tomltestdata/tcp-echo/go.modtestdata/tcp-echo/main.go
🚧 Files skipped from review as they are similar to previous changes (3)
- testdata/tcp-echo/go.mod
- api/core/schema.yml
- testdata/tcp-echo/.miren/app.toml
|
Ok this does the thing now. Ready for re-review! (I'll knock out the last few rabbit issues in parallel.) |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
appconfig/appconfig_test.go (1)
926-1137: Addnode_portboundary tests to lock in the new validation path.
Validate()now enforcesnode_portrange, but this suite doesn’t assert the-1/65536failure cases yet.✅ Suggested test additions
func TestValidatePortsConfig(t *testing.T) { + t.Run("node_port negative", func(t *testing.T) { + config := ` +name = "test-app" + +[[services.web.ports]] +port = 8080 +name = "http" +node_port = -1 +` + _, err := Parse([]byte(config)) + require.Error(t, err) + assert.Contains(t, err.Error(), "node_port must be between 0 and 65535") + }) + + t.Run("node_port out of range", func(t *testing.T) { + config := ` +name = "test-app" + +[[services.web.ports]] +port = 8080 +name = "http" +node_port = 70000 +` + _, err := Parse([]byte(config)) + require.Error(t, err) + assert.Contains(t, err.Error(), "node_port must be between 0 and 65535") + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@appconfig/appconfig_test.go` around lines 926 - 1137, Add two subtests inside TestValidatePortsConfig mirroring the existing port-range tests that call Parse on configs with [[services.web.ports]] entries using node_port = -1 and node_port = 65536, require an error, and assert the error message contains the node_port range failure (e.g. "node_port must be between 1 and 65535"). Locate the TestValidatePortsConfig function and add t.Run blocks similar to the "port out of range" and "port zero" cases that use Parse to trigger validation; keep assertions consistent with other cases (require.Error and assert.Contains on err.Error()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@controllers/deployment/launcher_test.go`:
- Around line 2076-2079: The comment describing
"TestNoServiceEntityForHTTPOnlyService" is placed above the wrong test; move the
HTTP-only comment block so it immediately precedes the
TestNoServiceEntityForHTTPOnlyService function and ensure the comment for
TestServiceEntityCreatedForScalarNonHTTPPort sits directly above that test;
update both comment texts if needed to add useful context (not restating code)
and verify the comment blocks reference the exact test names
TestNoServiceEntityForHTTPOnlyService and
TestServiceEntityCreatedForScalarNonHTTPPort so intent is clear when scanning
the tests.
---
Nitpick comments:
In `@appconfig/appconfig_test.go`:
- Around line 926-1137: Add two subtests inside TestValidatePortsConfig
mirroring the existing port-range tests that call Parse on configs with
[[services.web.ports]] entries using node_port = -1 and node_port = 65536,
require an error, and assert the error message contains the node_port range
failure (e.g. "node_port must be between 1 and 65535"). Locate the
TestValidatePortsConfig function and add t.Run blocks similar to the "port out
of range" and "port zero" cases that use Parse to trigger validation; keep
assertions consistent with other cases (require.Error and assert.Contains on
err.Error()).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
appconfig/appconfig.goappconfig/appconfig_test.gocontrollers/deployment/launcher.gocontrollers/deployment/launcher_test.gocontrollers/service/service.gocontrollers/service/service_test.go
| // TestNoServiceEntityForHTTPOnlyService verifies that services with only HTTP | ||
| // ports do not get a Service entity created (they use httpingress instead). | ||
| // TestServiceEntityCreatedForScalarNonHTTPPort tests that the launcher creates a | ||
| // Service entity when scalar port fields (Port/PortType) specify a non-HTTP port. |
There was a problem hiding this comment.
Misplaced test comment currently documents the wrong function.
The HTTP-only comment block is placed above the scalar non-HTTP test, which makes test intent harder to scan.
✏️ Suggested comment placement fix
-// TestNoServiceEntityForHTTPOnlyService verifies that services with only HTTP
-// ports do not get a Service entity created (they use httpingress instead).
// TestServiceEntityCreatedForScalarNonHTTPPort tests that the launcher creates a
// Service entity when scalar port fields (Port/PortType) specify a non-HTTP port.
func TestServiceEntityCreatedForScalarNonHTTPPort(t *testing.T) {
@@
}
+// TestNoServiceEntityForHTTPOnlyService verifies that services with only HTTP
+// ports do not get a Service entity created (they use httpingress instead).
func TestNoServiceEntityForHTTPOnlyService(t *testing.T) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // TestNoServiceEntityForHTTPOnlyService verifies that services with only HTTP | |
| // ports do not get a Service entity created (they use httpingress instead). | |
| // TestServiceEntityCreatedForScalarNonHTTPPort tests that the launcher creates a | |
| // Service entity when scalar port fields (Port/PortType) specify a non-HTTP port. | |
| // TestServiceEntityCreatedForScalarNonHTTPPort tests that the launcher creates a | |
| // Service entity when scalar port fields (Port/PortType) specify a non-HTTP port. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@controllers/deployment/launcher_test.go` around lines 2076 - 2079, The
comment describing "TestNoServiceEntityForHTTPOnlyService" is placed above the
wrong test; move the HTTP-only comment block so it immediately precedes the
TestNoServiceEntityForHTTPOnlyService function and ensure the comment for
TestServiceEntityCreatedForScalarNonHTTPPort sits directly above that test;
update both comment texts if needed to add useful context (not restating code)
and verify the comment blocks reference the exact test names
TestNoServiceEntityForHTTPOnlyService and
TestServiceEntityCreatedForScalarNonHTTPPort so intent is clear when scanning
the tests.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
docs/docs/traffic-routing.md (1)
77-77: Clarify the gRPC example wording.“gRPC without HTTP/2” is self-contradictory and may confuse readers; consider rephrasing to indicate gRPC traffic that bypasses HTTP ingress and is exposed as raw L4.
Suggested wording tweak
-For non-HTTP services — databases, game servers, IRC, gRPC without HTTP/2, raw TCP/UDP protocols — Miren routes traffic at Layer 4 using nftables NAT rules. +For non-HTTP services — databases, game servers, IRC, and other raw TCP/UDP protocols (including gRPC services exposed via L4 passthrough) — Miren routes traffic at Layer 4 using nftables NAT rules.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/docs/traffic-routing.md` at line 77, The phrase "gRPC without HTTP/2" is confusing; update the sentence that currently reads "For non-HTTP services — databases, game servers, IRC, gRPC without HTTP/2, raw TCP/UDP protocols — Miren routes traffic at Layer 4 using nftables NAT rules." to clarify gRPC that bypasses HTTP ingress and is exposed as raw L4 (e.g., "gRPC traffic exposed directly at Layer 4 (bypassing HTTP ingress/HTTP/2)") so readers understand you mean gRPC carried as raw L4 rather than implying gRPC lacks HTTP/2.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/docs/traffic-routing.md`:
- Around line 25-27: The fenced diagram blocks (e.g., the blocks containing
"Client → :443 → HTTP Ingress → Route Lookup → Activator → Sandbox :3000" and
"Client → :6697 (node_port) → nftables PREROUTING → Service Chain → Load Balance
→ Endpoint Chain → DNAT → Sandbox :6697", and the similar block at lines
127–130) are missing language identifiers and trigger MD040; update each
triple-backtick fence to include a language tag such as text (e.g., replace ```
with ```text) so the diagrams are annotated and the markdown linter passes.
---
Nitpick comments:
In `@docs/docs/traffic-routing.md`:
- Line 77: The phrase "gRPC without HTTP/2" is confusing; update the sentence
that currently reads "For non-HTTP services — databases, game servers, IRC, gRPC
without HTTP/2, raw TCP/UDP protocols — Miren routes traffic at Layer 4 using
nftables NAT rules." to clarify gRPC that bypasses HTTP ingress and is exposed
as raw L4 (e.g., "gRPC traffic exposed directly at Layer 4 (bypassing HTTP
ingress/HTTP/2)") so readers understand you mean gRPC carried as raw L4 rather
than implying gRPC lacks HTTP/2.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
docs/docs/firewall.mddocs/docs/services.mddocs/docs/traffic-routing.md
✅ Files skipped from review due to trivial changes (1)
- docs/docs/services.md
ff45ec6 to
761c4b4
Compare
evanphx
left a comment
There was a problem hiding this comment.
Looks great! I suspect there will be some "service in app.toml" vs "service entity" confusion until we change the name of the later, but that's fine for now.
|
|
||
| **HTTP Ingress (TCP 80/443):** Application traffic uses standard HTTP/HTTPS. Port 80 handles ACME certificate challenges and redirects to HTTPS. Port 443 serves your applications over TLS. | ||
|
|
||
| **NodePorts:** If your app exposes non-HTTP services with `node_port` in the port configuration, those ports must also be open. For example, an IRC server with `node_port = 6667` requires TCP port 6667 to be reachable. |
There was a problem hiding this comment.
Open and reachable in what sense? That the host machine doesn't have firewall rules prevent them?
There was a problem hiding this comment.
Yeah the section this is in sets the context:
Required External Ports
If you're running Miren on a cloud provider, you'll need to configure security groups or network ACLs to allow external traffic to reach the Miren server.
The lower layers (sandbox spec, firewall, service controller) already handled multi-port containers, but the app config layer was the bottleneck: one scalar port per service, no node_port, no protocol. Adds a ports[] component array to the config spec schema and wires it through the full pipeline — TOML parsing with validation in appconfig, mapping in the build server, config version conversion, and the deployment launcher. The existing scalar port/port_name/ port_type fields are preserved for backward compat; ports[] takes precedence when present, and mixing the two styles is a validation error. The PORT env var picks the first HTTP-typed port, or falls back to the first port in the array. Also adds deploy-time node port uniqueness validation: since node_port is now a first-class config field, two apps could silently claim the same host port and only discover the conflict when nftables failed at runtime. The new validateNodePorts check catches both intra-app duplicates and cross-app conflicts with clear error messages. MIR-745
The launcher now creates network_v1alpha.Service entities for app services that have non-HTTP ports (TCP/UDP). These entities are needed for L4 traffic routing via ipalloc → ServiceController → nftables. - ensureServiceForPorts: creates/updates Service entities with correct ports, match labels, and metadata; preserves existing IPs on update - cleanupStaleServices: removes orphaned Service entities when services are removed or become HTTP-only - Normalize empty port protocol to "tcp" before duplicate checks in appconfig - Add testdata/tcp-echo app for multi-port (HTTP health + TCP echo) testing
Previously, Create() used srv.Port[0] to determine the DNAT target port for all endpoint chains. For multi-port services, this meant all traffic was forwarded to the first port regardless of the original destination. Move endpoint chain setup inside the per-port loop so each service port gets its own endpoint chains targeting the correct port. Add a test that verifies multi-port services produce distinct endpoint chains per port.
…UDP support - Fix mixed-style detection to catch negative scalar port values (Port != 0) - Skip stale service cleanup when any ensureServiceForPorts call fails, preventing accidental deletion of active Service entities - Backfill scalar Port/PortName/PortType into Ports array in ensureServiceForPorts so legacy configs trigger Service entity creation - Wire protocol through ServiceController nftables rules instead of hardcoding TCP, enabling UDP service routing
New page covering how traffic reaches Miren sandboxes: HTTP routing through the ingress, L4 TCP/UDP routing through nftables, multi-port services, NodePorts, the PORT env var, and internal service communication. Updates services.md and firewall.md to cross-reference the new page.
The user-facing PortConfig had two fields (type for routing mode, protocol for transport) which created confusing configs like type="tcp", protocol="udp". Since HTTP is always TCP, these collapse naturally into a single type field with values "http", "tcp", "udp". Internal entity schemas retain their protocol field — it's now derived automatically from type at the mapping boundary in the build server.
- Add ports section to app-toml.md (was missing from the field reference) - Add traffic routing subsection to app-configuration.md guide - Add app.toml reference link to traffic-routing.md next steps - Replace jargon "scalar" with "single-port shorthand" in docs
761c4b4 to
c954f67
Compare
Yeah we can finalize a name and land #611 soon |
Users have been asking to deploy non-HTTP services — the immediate motivation was wanting to run an IRC server on Miren with ports 6667 and 6697 exposed. Today the app config layer only supports a single port per service, and there's no way to specify
node_portorprotocol.The fun part is that all the hard infrastructure work was already done. The sandbox container spec, firewall rules, and service controller all handle multi-port just fine. The bottleneck was the app config layer: one
portinteger per service, no array, nonode_port, noprotocol.Config layer (
appconfig)Adds a
ports[]array to the config spec schema and wires it through the full pipeline — TOML parsing with validation in appconfig, mapping through the build server'sbuildServicesConfig, config version conversion, and the deployment launcher'sbuildSandboxSpec. The existing scalarport/port_name/port_typefields are preserved for backward compat;ports[]takes precedence when present, and mixing the two styles is a validation error. ThePORTenv var picks the first HTTP-typed port, or falls back to the first port in the array.Also adds a deploy-time guardrail: since
node_portis now a first-class config field, two apps could silently claim the same host port and only find out when nftables blew up at runtime. The newvalidateNodePortscheck runs duringBuildFromTarand catches both intra-app duplicates and cross-app conflicts with clear error messages.Empty protocol is now normalized to
"tcp"before duplicate(port, protocol)validation.Service entity lifecycle (
controllers/deployment/launcher)HTTP services are proxied at L7 via httpingress, but non-HTTP services (TCP/UDP) need L4 routing through the ipalloc → ServiceController → nftables pipeline. That pipeline requires a
network_v1alpha.Serviceentity to exist — without one, no IP is allocated and no firewall rules are created.The launcher now manages Service entities for app services that have at least one non-HTTP port:
ensureServiceForPorts— creates or updates a Service entity with the correct ports, match labels, and metadata. On update, preserves existing IPs so ipalloc doesn't need to re-allocate.cleanupStaleServices— deletes orphaned Service entities when services are removed from config or become HTTP-only.svc/<appname>-<servicename>) for stable identity across deploys.ServiceController per-port endpoint chain fix (
controllers/service)The ServiceController had a bug where
Create()usedsrv.Port[0]to determine the DNAT target port for all endpoint chains. For multi-port services, this meant all traffic was forwarded to the first port regardless of the original destination port — e.g., TCP traffic arriving on port 7000 would get DNAT'd to port 3000.Moved endpoint chain setup inside the per-port loop so each service port gets its own endpoint chains targeting the correct port.
Test app
Adds
testdata/tcp-echo/— a minimal Go app with a TCP echo server on port 7000 and an HTTP health endpoint on port 3000, for validating multi-port service behavior end-to-end.Example app config for an IRC server:
Closes MIR-745