Skip to content

Conversation

wdbaruni
Copy link
Member

@wdbaruni wdbaruni commented Feb 19, 2025

This PR introduces networking capabilities to Bacalhau jobs, enabling containerized workloads to expose services and communicate with host networks. This is a significant enhancement that opens up new use cases for Bacalhau and enabling inter-job communication.

Core Changes

1. Network Modes

  • Introduced explicit network configuration in job specifications
  • Supported modes:
    • bridge
    • host: (Linux only) Direct access to host network stack
    • none: Complete network isolation
  • Renamed legacy 'full' mode to more accurate 'host' mode

2. Port Mapping System

  • Introduced PortMapping configuration:
    type PortMapping struct {
        Name   string // Required identifier for the port
        Static int    // Optional host port (auto-allocated if not specified)
        Target int    // Container port to expose (only in bridge mode)
    }
  • Added port allocation system to manage host ports:
    • Support for both static (user-defined) and dynamic (auto-allocated) ports
    • Conflict detection and resolution
    • Port range configuration for dynamic allocation
    • Port release on job completion

3. Environment Variables

Added standardized environment variables for port discovery:

  • BACALHAU_HOST_PORT_{name}: Host port mapping
  • BACALHAU_PORT_{name}: Container port mapping

Example Usage:

network:
 type: bridge
 ports:
  - name: http
    static: 8080 # Host port
    target: 80 # Container port
  - name: metrics
    target: 9090 # Dynamic host port allocation

Testing:

  • Added tests for port mapping in both host and bridge modes
  • Added tests for environment variable injection
  • Added tests for host service accessibility

Future Work:

  1. Auto-discovery of compute node IP addresses
  2. Service discovery system for job intercommunication

Summary by CodeRabbit

  • New Features

    • Enhanced networking with dynamic port allocation and support for both host and isolated bridge modes.
    • Improved compute node configuration by exposing advertised address details for better connectivity.
    • Added new properties and definitions for network configurations and port mappings in the API.
    • Introduced a comprehensive suite of integration tests for networking functionalities, validating various configurations and scenarios.
    • Added validation for network configurations during task submissions to ensure correctness.
  • Refactor

    • Streamlined network configuration handling and parameter management across the system.
  • Tests

    • Expanded test coverage for network configurations and port mapping scenarios, including new tests for various Docker networking modes and validation of port allocation behaviors.
    • Introduced new validation tests for task network configurations.
    • Added a new test suite for execution port allocation functionality.
    • Established a new integration test suite for networking functionalities.
  • Documentation

    • Updated API and configuration guides to reflect the new network capabilities and port mapping definitions.

Copy link

linear bot commented Feb 19, 2025

ENG-624 Job port mapping

Copy link
Contributor

coderabbitai bot commented Feb 19, 2025

Walkthrough

This pull request introduces several network-related modifications across the codebase. Test cases have been updated to replace references to models.NetworkFull with models.NetworkHost and add coverage for bridge mode. A new port allocation system is implemented with its interface, tests, and integration into the executor’s workflow. Additional environment variable handling for network ports, configuration enhancements in defaults and compute types, and updated Swagger API documentation are also included. Minor dependency updates and test utility enhancements complete the changes.

Changes

File(s) Change Summary
cmd/cli/docker/docker_run_cli_test.go, pkg/bidstrategy/semantic/networking_allowlist_test.go, pkg/bidstrategy/semantic/networking_test.go, pkg/executor/docker/executor_test.go Updated test assertions and cases: replaced models.NetworkFull with models.NetworkHost; added new Docker networking tests (host and bridge mode) and dynamic port mapping tests.
go.mod Updated dependency for github.com/docker/go-connections (version update and indirect dependency cleanup).
pkg/compute/envvars.go, pkg/compute/envvars_test.go Added logic to include network port mappings in environment variables and corresponding tests for executions with network ports.
pkg/compute/executor.go, pkg/models/execution.go, pkg/test/executor/test_runner.go Integrated PortAllocator into executor: added new fields, updated argument preparation, port allocation and release logic; enhanced test runner to use the new base executor.
pkg/compute/port_allocator.go, pkg/compute/port_allocator_test.go, pkg/compute/types.go Introduced a new port allocation system with support for static and dynamic port assignments, including a corresponding interface and comprehensive test suite.
pkg/compute/node_info_decorator.go, pkg/models/node_info.go, pkg/node/compute.go Added advertised address support in node information and updated node initialization to incorporate port allocator and advertised address.
pkg/executor/docker/executor.go, pkg/executor/docker/network.go Refactored Docker networking setup functions: consolidated parameters, adjusted network mode handling by removing NetworkFull and adding support for NetworkHost and NetworkBridge.
pkg/models/network.go, pkg/models/network_string.go, pkg/config/defaults.go, pkg/config/types/compute.go Updated network models and configuration: replaced NetworkFull with NetworkHost, added NetworkBridge, introduced PortMapping type, and extended configuration structs with new network settings.
pkg/swagger/docs.go, pkg/swagger/swagger.json, webui/lib/api/schema/swagger.json Upgraded API documentation: added new definitions for models.PortMapping and models.NetworkConfig, included an address property in compute node info, and updated network enumeration.
pkg/test/utils/platform.go Added a new utility function SkipIfNotLinux to conditionally skip tests on non-Linux platforms.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Executor
    participant PortAllocator
    participant NetworkSetup
    participant Container

    Client->>Executor: Submit RunCommandRequest
    Executor->>PortAllocator: AllocatePorts(execution)
    PortAllocator-->>Executor: Return port mappings
    Executor->>NetworkSetup: setupNetworkForJob(params)
    NetworkSetup-->>Executor: Configure network (host/bridge)
    Executor->>Container: Start container with mapped ports
    Container-->>Executor: Execution complete
    Executor->>PortAllocator: ReleasePorts(execution)
Loading

Possibly related PRs

  • prevent port number reuse with TTL-based caching #4689: The changes in the main PR, which involve modifications to network configurations and port allocations, are related to the retrieved PR that introduces a TTL-based caching mechanism for port allocation, as both PRs focus on enhancing the management of network ports and configurations.

🐇 In the code we hop and play,
Ports and networks lead the way.
From full to host, we now can see,
A bridge of paths, so wild and free!
With tests in tow, we leap with glee,
A rabbit's joy in code harmony! 🐇

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 golangci-lint (1.62.2)

{"Issues":[{"FromLinter":"typecheck","Text":"pattern build/: no matching files found","Severity":"","SourceLines":["//go:embed build/"],"Replacement":null,"Pos":{"Filename":"webui/webui.go","Offset":0,"Line":23,"Column":12},"ExpectNoLint":false,"ExpectedNoLintLinter":""}],"Report":{"Linters":[{"Name":"asasalint"},{"Name":"asciicheck"},{"Name":"bidichk"},{"Name":"bodyclose","Enabled":true},{"Name":"canonicalheader"},{"Name":"containedctx"},{"Name":"contextcheck"},{"Name":"copyloopvar","Enabled":true},{"Name":"cyclop"},{"Name":"decorder"},{"Name":"deadcode"},{"Name":"depguard","Enabled":true},{"Name":"dogsled","Enabled":true},{"Name":"dupl"},{"Name":"dupword"},{"Name":"durationcheck"},{"Name":"errcheck","Enabled":true,"EnabledByDefault":true},{"Name":"errchkjson"},{"Name":"errname"},{"Name":"errorlint"},{"Name":"execinquery"},{"Name":"exhaustive"},{"Name":"exhaustivestruct"},{"Name":"exhaustruct"},{"Name":"exportloopref"},{"Name":"forbidigo","Enabled":true},{"Name":"forcetypeassert"},{"Name":"fatcontext"},{"Name":"funlen","Enabled":true},{"Name":"gci"},{"Name":"ginkgolinter"},{"Name":"gocheckcompilerdirectives"},{"Name":"gochecknoglobals"},{"Name":"gochecknoinits","Enabled":true},{"Name":"gochecksumtype"},{"Name":"gocognit"},{"Name":"goconst","Enabled":true},{"Name":"gocritic"},{"Name":"gocyclo","Enabled":true},{"Name":"godot"},{"Name":"godox"},{"Name":"err113"},{"Name":"gofmt","Enabled":true},{"Name":"gofumpt"},{"Name":"goheader"},{"Name":"goimports","Enabled":true},{"Name":"golint"},{"Name":"mnd","Enabled":true},{"Name":"gomnd"},{"Name":"gomoddirectives"},{"Name":"gomodguard"},{"Name":"goprintffuncname","Enabled":true},{"Name":"gosec","Enabled":true},{"Name":"gosimple","Enabled":true,"EnabledByDefault":true},{"Name":"gosmopolitan"},{"Name":"govet","Enabled":true,"EnabledByDefault":true},{"Name":"grouper"},{"Name":"ifshort"},{"Name":"iface"},{"Name":"importas"},{"Name":"inamedparam"},{"Name":"ineffassign","Enabled":true,"EnabledByDefault":true},{"Name":"interfacebloat"},{"Name":"interfacer"},{"Name":"intrange"},{"Name":"ireturn"},{"Name":"lll","Enabled":true},{"Name":"loggercheck"},{"Name":"maintidx"},{"Name":"makezero"},{"Name":"maligned"},{"Name":"mirror"},{"Name":"misspell"},{"Name":"musttag"},{"Name":"nakedret","Enabled":true},{"Name":"nestif"},{"Name":"nilerr"},{"Name":"nilnil"},{"Name":"nlreturn"},{"Name":"noctx","Enabled":true},{"Name":"nonamedreturns"},{"Name":"nosnakecase"},{"Name":"nosprintfhostport"},{"Name":"paralleltest"},{"Name":"perfsprint"},{"Name":"prealloc"},{"Name":"predeclared"},{"Name":"promlinter"},{"Name":"protogetter"},{"Name":"reassign"},{"Name":"recvcheck"},{"Name":"revive"},{"Name":"rowserrcheck"},{"Name":"sloglint"},{"Name":"scopelint"},{"Name":"sqlclosecheck"},{"Name":"spancheck"},{"Name":"staticcheck","Enabled":true,"EnabledByDefault":true},{"Name":"structcheck"},{"Name":"stylecheck","Enabled":true},{"Name":"tagalign"},{"Name":"tagliatelle"},{"Name":"tenv"},{"Name":"testableexamples"},{"Name":"testifylint"},{"Name":"testpackage"},{"Name":"thelper"},{"Name":"tparallel"},{"Name":"typecheck","Enabled":true,"EnabledByDefault":true},{"Name":"unconvert","Enabled":true},{"Name":"unparam"},{"Name":"unused","Enabled":true,"EnabledByDefault":true},{"Name":"usestdlibvars"},{"Name":"varcheck"},{"Name":"varnamelen"},{"Name":"wastedassign"},{"Name":"whitespace","Enabled":true},{"Name":"wrapcheck"},{"Name":"wsl"},{"Name":"zerologlint"},{"Name":"nolintlint","Enabled":true}]}}

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 28

🔭 Outside diff range comments (2)
webui/lib/api/schema/swagger.json (1)

1879-1893: 🧹 Nitpick (assertive)

Update Network enum documentation.

The models.Network enum has been updated to replace NetworkFull with NetworkHost and add NetworkBridge. Consider adding descriptions for each enum value to improve API documentation.

Add descriptions using the x-enum-descriptions field:

         "x-enum-varnames": [
             "NetworkNone",
             "NetworkHost",
             "NetworkHTTP",
             "NetworkBridge"
+        ],
+        "x-enum-descriptions": [
+            "No network access",
+            "Direct access to host network stack (Linux only)",
+            "Restricted HTTP(S) access to specified domains",
+            "Isolated network with port mapping support"
         ]
pkg/swagger/swagger.json (1)

1880-1892: 🧹 Nitpick (assertive)

Update the Network enumeration values.
The updated models.Network enum now lists: NetworkNone, NetworkHost, NetworkHTTP, and NetworkBridge. Note that the legacy mode has been replaced by NetworkHost and a new NetworkBridge has been added. However, the appearance of NetworkHTTP is not mentioned in the PR summary (which specifies only three network modes: bridge, host, none). Please verify that the inclusion of NetworkHTTP is intentional and that all references in the codebase (and documentation) have been updated accordingly.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea31142 and 526a9b7.

⛔ Files ignored due to path filters (1)
  • webui/lib/api/generated/types.gen.ts is excluded by !**/generated/**
📒 Files selected for processing (26)
  • cmd/cli/docker/docker_run_cli_test.go (1 hunks)
  • go.mod (1 hunks)
  • pkg/bidstrategy/semantic/networking_allowlist_test.go (2 hunks)
  • pkg/bidstrategy/semantic/networking_test.go (2 hunks)
  • pkg/compute/envvars.go (1 hunks)
  • pkg/compute/envvars_test.go (1 hunks)
  • pkg/compute/executor.go (8 hunks)
  • pkg/compute/node_info_decorator.go (4 hunks)
  • pkg/compute/port_allocator.go (1 hunks)
  • pkg/compute/port_allocator_test.go (1 hunks)
  • pkg/compute/types.go (1 hunks)
  • pkg/config/defaults.go (1 hunks)
  • pkg/config/types/compute.go (2 hunks)
  • pkg/executor/docker/executor.go (1 hunks)
  • pkg/executor/docker/executor_test.go (3 hunks)
  • pkg/executor/docker/network.go (3 hunks)
  • pkg/models/execution.go (1 hunks)
  • pkg/models/network.go (8 hunks)
  • pkg/models/network_string.go (1 hunks)
  • pkg/models/node_info.go (1 hunks)
  • pkg/node/compute.go (4 hunks)
  • pkg/swagger/docs.go (6 hunks)
  • pkg/swagger/swagger.json (6 hunks)
  • pkg/test/executor/test_runner.go (2 hunks)
  • pkg/test/utils/platform.go (1 hunks)
  • webui/lib/api/schema/swagger.json (6 hunks)
🧰 Additional context used
🪛 ast-grep (0.31.1)
pkg/executor/docker/executor_test.go

[warning] 792-792: "Detected a network listener listening on 0.0.0.0 or an empty string.
This could unexpectedly expose the server publicly as it binds to all
available interfaces. Instead, specify another IP address that is not
0.0.0.0 nor the empty string."
Context: net.Listen("tcp", ":0")
Note: [CWE-200] Exposure of Sensitive Information to an Unauthorized Actor [REFERENCES]
- https://owasp.org/Top10/A01_2021-Broken_Access_Control

(avoid-bind-to-all-interfaces-go)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: build / Build Binary (windows, amd64)
  • GitHub Check: build / Build Binary (darwin, arm64)
  • GitHub Check: build / Build Binary (darwin, amd64)
  • GitHub Check: build / Build Binary (linux, armv6)
  • GitHub Check: build / Build Binary (linux, armv7)
  • GitHub Check: build / Build Binary (linux, arm64)
  • GitHub Check: lint / lint
  • GitHub Check: build / Build Binary (linux, amd64)
🔇 Additional comments (38)
pkg/compute/port_allocator.go (3)

55-109: Potential leftover used ports on partial failure
The cleanup mechanism is good. However, if AllocatePorts allocates some ports but fails midway, are you certain all partially allocated ports are freed? This logic appears to handle that (cleanup() function), but double-check if any subsequent error path might skip the cleanup() call.


165-175: Port release workflow
ReleasePorts successfully cleans up allocated ports from both the global map and the per-execution map. Looks correct. Ensure that if an execution ID is reused or re-run, it does not rely on previously allocated ports if it was never properly released. Monitoring code flow for a guaranteed call to ReleasePorts upon job completion is crucial.


177-178: Nice interface compliance check
Verifying var _ PortAllocator = &portAllocator{} is helpful for compile-time guarantees.

pkg/executor/docker/network.go (3)

71-74: Confirm usage of single struct parameter
Adopting params *executor.RunCommandRequest simplifies parameter passing, but ensure that all dependent fields (e.g., environment, volumes, etc.) remain accessible or require no further changes.


75-75: Toggle NetworkDisabled explicitly
containerConfig.NetworkDisabled = params.Network.Disabled() is clear. Just be sure that you never need partial or device-only networking. If partial network usage arises in the future, you might need more granular toggles.


114-114: Re-check references to job vs. executionID
Passing params.JobID, params.ExecutionID, params.Network to createHTTPGateway is correct, but confirm if there are other references to the old signature in this file or tests.

pkg/models/network.go (7)

21-23: Renamed NetworkFull to NetworkHost
Renaming improves clarity. The extra comment specifying direct host network access is helpful for maintainers.


56-59: Bridge mode addition
Introducing NetworkBridge clarifies default container networking. This is a solid addition, ensuring bridging is recognized explicitly in the code.


92-95: Ports field is a valuable extension
Adding Ports to NetworkConfig is a great enhancement. This paves the way for more flexible networking configurations.


114-115: Ensure nil-slice normalization
Lines 114-115: Initializing n.Ports to an empty slice avoids nil pointer checks later. Good housekeeping.


136-185: Strict domain rules
The additional domain checks are thorough. The logic that Domains are only valid for NetworkHTTP is consistent with the intended design. Mirror this approach for Ports usage for other modes.


295-310: PortMapping struct
Names must start with a letter. This is a good practice to avoid environment variable conflicts. The separation between Static and Target clarifies host vs. container concepts.


312-341: Validate method
Validation ensures ports remain in 0–65535 range. That’s crucial. The name pattern is also enforced, which helps unify environment variable naming. This is well-designed.

pkg/swagger/docs.go (6)

1888-1889: Enum values for network modes are consistent.

The updated enumeration for NetworkBridge aligns with the revised network type definitions.


1893-1895: Enum varnames alignment confirmed.

Adding "NetworkHost", "NetworkHTTP", and "NetworkBridge" consistently reflects the newly introduced network modes.


1907-1913: Validate port usage for different network modes.

While the Ports property is well-documented, ensure that any constraints (e.g., requiring Bridge mode to set container ports) are correctly enforced at runtime.


2030-2046: PortMapping documentation is clear.

The field descriptions are thorough. Verify that the uniqueness of the Name field is enforced if needed.


2575-2582: Network configuration field addition is consistent.

This addition meets the PR's objective of configurable networking. Be mindful of default behaviors in tasks that do not specify network requirements.


2902-2918: Port range configuration is well-defined.

Introducing AdvertisedAddress and the port range parameters aligns with dynamic port allocation. Ensure that collisions are handled robustly.

pkg/test/utils/platform.go (1)

8-15: Provide cross-platform alternatives where needed.

This helper is effective for Linux-only tests. Consider whether separate logic or alternative tests might be necessary on other platforms.

pkg/models/network_string.go (2)

12-14: Enumeration references are updated.

The added references to NetworkHost and NetworkBridge are consistent with the newly defined constants.


17-19: String representation indices updated.

Renaming _Network_name and adjusting _Network_index for the new enum entry is correct. Double-check index boundaries to avoid off-by-one errors.

pkg/bidstrategy/semantic/networking_test.go (1)

36-38: LGTM! Network type updates align with the new networking model.

The changes correctly reflect the renaming of 'full' mode to 'host' mode while maintaining the same test coverage and validation logic.

pkg/bidstrategy/semantic/networking_allowlist_test.go (1)

38-43: LGTM! Network type updates maintain domain filtering behavior.

The changes consistently reflect the renaming of network types while preserving the same domain filtering logic and test coverage.

pkg/compute/node_info_decorator.go (1)

44-44: LGTM! Node address advertisement implementation.

The implementation correctly handles the advertised address field, enabling node discovery as described in the PR objectives.

Also applies to: 62-62

pkg/compute/types.go (1)

51-64: Well-designed interface for port allocation!

The PortAllocator interface is well-documented and follows good design principles:

  • Clear separation of concerns between allocation and release
  • Comprehensive documentation of behavior
  • Thread-safety consideration for concurrent allocations
  • Proper error handling for allocation failures
pkg/compute/envvars_test.go (2)

149-186: LGTM! Well-structured test case for network ports.

The test case thoroughly validates the environment variable generation for network ports, including:

  • Named port mappings with different target ports
  • Port mapping with same target port
  • Multiple port mappings in a single execution

187-213: LGTM! Good negative test case.

The test case correctly verifies that no port environment variables are set when a network configuration exists but has no ports defined.

pkg/compute/port_allocator_test.go (3)

15-21: LGTM! Well-structured test suite setup.

The test suite is properly set up using the testify/suite package, which provides a clean and organized way to group related test cases.


23-45: LGTM! Good negative test cases.

The test cases correctly verify that empty mappings are returned when:

  • No network configuration is provided
  • Network type is set to None

47-83: LGTM! Comprehensive port allocation test.

The test case thoroughly validates both static and dynamic port allocation scenarios:

  • Dynamic port allocation within range
  • Static port outside range
  • Static port inside range
pkg/node/compute.go (1)

108-114: LGTM! Port allocator initialization looks good.

The port allocator is properly initialized with configuration parameters and includes error handling.

pkg/compute/executor.go (1)

181-193: LGTM! Port allocation and cleanup are properly handled.

The implementation:

  1. Allocates ports using the port allocator
  2. Updates the execution with allocated ports
  3. Includes cleanup to release ports after execution
pkg/executor/docker/executor.go (1)

370-370: LGTM! Simplified network setup function call.

The refactoring improves maintainability by passing the entire request object instead of individual fields.

cmd/cli/docker/docker_run_cli_test.go (1)

405-405: LGTM! Updated network type assertion.

The test has been correctly updated to use models.NetworkHost instead of models.NetworkFull, aligning with the PR's objective to rename the 'full' mode to 'host' for better clarity.

pkg/swagger/swagger.json (3)

1377-1380: Add the address property in ComputeNodeInfo.
The new address property under models.ComputeNodeInfo provides the network location (IPv4 or hostname) where the compute node can be reached, which aligns with the enhanced network configuration requirements. Ensure that downstream components correctly consume and validate this value.


1903-1909: Introduce the Ports property in NetworkConfig.
Adding the Ports property to the models.NetworkConfig definition enables tasks to specify port mappings, as required for dynamic and static port allocation. Ensure that clients populating this field also conform to the schema defined in models.PortMapping.


2898-2914: Enhance types.NetworkConfig with AdvertisedAddress and port range settings.
The updated types.NetworkConfig now includes:
AdvertisedAddress – the address advertised to other nodes (with auto-discovery as a fallback),
PortRangeStart and PortRangeEnd – defining the inclusive range from which ports can be allocated to jobs.

These additions improve the network configuration capabilities for compute nodes. Please verify that the rest of the system (e.g. dynamic port allocation logic) correctly consumes and enforces these new configurations.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 526a9b7 and 4a2ad8c.

📒 Files selected for processing (1)
  • pkg/test/devstack/networking_test.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: build / Build Binary (windows, amd64)
  • GitHub Check: build / Build Binary (darwin, arm64)
  • GitHub Check: build / Build Binary (darwin, amd64)
  • GitHub Check: build / Build Binary (linux, armv6)
  • GitHub Check: build / Build Binary (linux, armv7)
  • GitHub Check: build / Build Binary (linux, arm64)
  • GitHub Check: build / Build Binary (linux, amd64)
  • GitHub Check: lint / lint
🔇 Additional comments (11)
pkg/test/devstack/networking_test.go (11)

1-4: Build tag and package definition look good.
No issues with the //go:build integration || !unit approach for integration tests.


5-24: Imports appear correct and consistent.
All imported packages seem necessary for integration tests.


25-33: Test suite struct initialization is straightforward.
No concurrency or initialization issues detected when storing the server and port fields.


69-73: Graceful teardown.
Closing the HTTP server in TearDownSuite ensures no orphan resources remain.


75-95: Scenario creation is well-structured.
The helper function nicely configures DevStack with the required network settings for each test.


97-114: Host networking test is validated properly.
Skipping on non-Linux platforms is appropriate, and verifying the "OK" response confirms local server accessibility.


116-131: Bridge networking test mirrors host mode checks.
The usage of host.docker.internal to reach the local server is correct for bridge mode.


133-151: Network none test ensures isolation.
Verifying an empty stdout and curl's exit code 7 confirms that no external networking is available.


191-228: Host mode port mapping test is thorough.
Confirmed that the container port can be reached directly on the host.


230-251: Privileged port test is a solid negative scenario.
Verifies that mapping to restricted ports fails as expected in bridge mode.


153-189:

❓ Verification inconclusive

Bridge mode port mapping is correctly validated.
Good approach to verify the mapped host port. One minor check: ensure nc is available in busybox:1.37.0.


🏁 Script executed:

#!/bin/bash
# Confirm that busybox:1.37.0 includes nc
docker run --rm busybox:1.37.0 which nc

Length of output: 138


Action Required: Manually verify the availability of nc in busybox:1.37.0.
Bridge mode port mapping is validated and the testing approach is solid. However, we could not automatically confirm that the busybox:1.37.0 image includes the nc command because the automated environment did not have Docker available. Please verify manually that nc is present in the image to ensure the test’s reliability.

@wdbaruni wdbaruni requested a review from jamlo February 19, 2025 11:20
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🔭 Outside diff range comments (1)
pkg/models/task_test.go (1)

19-46: 🧹 Nitpick (assertive)

Consider adding more network normalization test cases.

The test could be expanded to verify normalization of:

  • Different network types
  • Port mapping defaults
  • Domain configurations
♻️ Duplicate comments (1)
pkg/test/devstack/networking_test.go (1)

35-67: 🧹 Nitpick (assertive)

Consider adding retry mechanism for port allocation.

While the Eventually block ensures server readiness, the port allocation could fail in concurrent test environments.

Consider wrapping the port allocation in a retry mechanism:

-	// Get a free port
-	port, err := network.GetFreePort()
-	s.Require().NoError(err)
-	s.port = port
+	// Get a free port with retry
+	var port int
+	s.Require().Eventually(func() bool {
+		var err error
+		port, err = network.GetFreePort()
+		return err == nil
+	}, 5*time.Second, 100*time.Millisecond)
+	s.port = port
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8906d6 and d51d52e.

📒 Files selected for processing (8)
  • pkg/compute/port_allocator.go (1 hunks)
  • pkg/lib/validate/numbers.go (1 hunks)
  • pkg/lib/validate/numbers_test.go (1 hunks)
  • pkg/models/network.go (8 hunks)
  • pkg/models/network_test.go (0 hunks)
  • pkg/models/task.go (1 hunks)
  • pkg/models/task_test.go (5 hunks)
  • pkg/test/devstack/networking_test.go (1 hunks)
💤 Files with no reviewable changes (1)
  • pkg/models/network_test.go
🧰 Additional context used
🪛 GitHub Check: lint / go-lint (ubuntu-latest)
pkg/models/network.go

[failure] 336-336:
The line is 155 characters long, which exceeds the maximum of 140 characters. (lll)


[failure] 340-340:
Magic number: 256, in detected (mnd)


[failure] 346-346:
Magic number: 1024, in detected (mnd)


[failure] 349-349:
Magic number: 65535, in detected (mnd)

🪛 GitHub Actions: Main Pipeline
pkg/models/network.go

[error] 336-336: The line is 155 characters long, which exceeds the maximum of 140 characters. (lll)

🔇 Additional comments (15)
pkg/lib/validate/numbers.go (2)

41-49: LGTM! Well-implemented less-than comparison.

The implementation is clean, follows the established pattern, and includes clear documentation.


51-59: LGTM! Well-implemented less-than-or-equal comparison.

The implementation is clean, follows the established pattern, and includes clear documentation.

pkg/lib/validate/numbers_test.go (2)

119-145: LGTM! Comprehensive test coverage for IsLessThan.

The test cases cover all essential scenarios including boundary conditions and different numeric types.


147-173: LGTM! Comprehensive test coverage for IsLessOrEqual.

The test cases cover all essential scenarios including boundary conditions and different numeric types.

pkg/compute/port_allocator.go (4)

22-31: Consider clarifying concurrency usage in code comments.

Adding a short note in the struct-level comments about how the mutex protects against concurrent allocations would help future maintainers understand the concurrency guarantees at a glance.


119-136: Consider providing an ephemeral fallback for static ports.

When a static port is specified but unavailable at runtime, the allocator currently errors out. As previously mentioned in past reviews, having an optional fallback to a dynamic port (when the user has not strictly required the specific port) can improve resiliency under port conflicts.


127-132: Remember “time-of-check vs. time-of-use” pitfalls for port binding.

By calling net.Listen here, there is still a small window where a previously open port can be claimed by another process. This can become more common under heavy system load or parallel job submissions. Consider documenting these edge cases or adopting an OS-allocated ephemeral port approach for robust collision avoidance.


138-182: Validate retry or backoff logic for high-scale port collisions.

The round-robin search is straightforward but may still collide if the system is under load and many ports are temporarily bound. Past reviews have highlighted this approach as generally good, yet you might consider a retry mechanism or a short backoff if collisions are frequent at scale.

pkg/models/network.go (2)

21-23: Rename from NetworkFull to NetworkHost is clear and consistent.

Renaming the legacy “full” mode to NetworkHost clarifies the intent. This change is a welcome improvement for readability and future maintenance.


56-59: The addition of NetworkBridge is well-structured.

Defining an explicit NetworkBridge mode is a clear approach to containerized network isolation. This is beneficial for clarity and extends the range of supported network setups.

pkg/models/task.go (1)

158-160: Network validation addition looks correct.

Ensuring the network configuration is valid early in the submission lifecycle reduces potential runtime errors and maintains consistency with other validated fields.

pkg/test/devstack/networking_test.go (2)

75-95: LGTM! Well-structured helper method.

The helper method effectively sets up test scenarios with networking enabled.


191-228: Same cleanup concern as bridge mode test.

The cleanup recommendation from the bridge mode test applies here as well.

pkg/models/task_test.go (2)

216-280: LGTM! Comprehensive network validation test cases.

The test cases effectively cover various network validation scenarios including:

  • Invalid network types
  • Port mapping validation
  • Domain validation

320-357: LGTM! Thorough deep copy verification.

The test effectively ensures that network configuration and port mappings are properly deep copied.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🔭 Outside diff range comments (1)
pkg/swagger/docs.go (1)

1883-1897: 🧹 Nitpick (assertive)

Add descriptions for network types.

Consider adding descriptions for each network type in the enum to help API consumers understand when to use each mode. For example:

  • NetworkNone: Complete network isolation
  • NetworkHost: Direct access to host network stack
  • NetworkBridge: Isolated network with port mapping support
  • NetworkHTTP: HTTP-only network access
♻️ Duplicate comments (7)
pkg/compute/port_allocator.go (5)

13-22: 🧹 Nitpick (assertive)

Consider documenting concurrency behavior more explicitly.
You already use a mutex, but adding a brief comment explaining how concurrent allocations are handled helps future maintainers understand the thread-safety guarantees.


24-44: 🛠️ Refactor suggestion

Validate the start port’s upper bound.
Though you check start >= MinimumPort and end <= MaximumPort, you don’t verify start <= MaximumPort. This can silently allow invalid ranges if start is above the max port.

 if err := errors.Join(
     validate.IsGreaterOrEqual(start, models.MinimumPort, "start port must be >= %d", models.MinimumPort),
     validate.IsLessOrEqual(end, models.MaximumPort, "end port must be <= %d", models.MaximumPort),
+    validate.IsLessOrEqual(start, models.MaximumPort, "start port must be <= %d", models.MaximumPort),
     validate.IsLessThan(start, end, "start port must be less than end port"),
 ); err != nil {

46-108: 🧹 Nitpick (assertive)

Address race conditions in port checking.
Using net.Listen() to verify port availability can still introduce a time-of-check vs. time-of-use race if another process binds that port between checks. Consider ephemeral port binding or more robust collision handling when concurrency is high.


110-127: 🧹 Nitpick (assertive)

Offer fallback for unavailable static ports.
Currently, if a requested static port is taken, you return an error. Some users might want fallback to an ephemeral port (unless they strictly require the static port). Consider making this configurable.


129-173: 🧹 Nitpick (assertive)

Revisit single-pass round-robin logic.
If you’re near the end of the range and do one modulo-based pass, collisions can occur under high load. You might want a retry or backoff mechanism for better long-term resilience.

pkg/compute/envvars.go (1)

57-67: ⚠️ Potential issue

Port name validation needed.

The implementation correctly adds environment variables for port discovery. However, port names are used directly in environment variable names without validation.

Apply this diff to validate port names:

 if execution.Job.Task().Network != nil {
     for _, port := range execution.Job.Task().Network.Ports {
+        // Validate port name contains only valid characters for environment variables
+        if !isValidEnvVarName(port.Name) {
+            return nil, fmt.Errorf("invalid port name %q: must contain only alphanumeric and underscore characters", port.Name)
+        }
         if port.Static > 0 {
             sysEnv[models.EnvVarHostPortPrefix+port.Name] = fmt.Sprintf("%d", port.Static)
         }
pkg/compute/port_allocator_test.go (1)

47-83: 🧹 Nitpick (assertive)

Consider adding more edge cases.

While the test covers basic functionality well, consider adding these edge cases:

  1. Invalid port numbers (negative ports, ports > 65535)
  2. Duplicate port names
  3. Empty port names
  4. Concurrent port allocation
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d51d52e and 7f90986.

⛔ Files ignored due to path filters (1)
  • webui/lib/api/generated/types.gen.ts is excluded by !**/generated/**
📒 Files selected for processing (10)
  • cmd/cli/docker/docker_run_cli_test.go (2 hunks)
  • pkg/compute/envvars.go (1 hunks)
  • pkg/compute/port_allocator.go (1 hunks)
  • pkg/compute/port_allocator_test.go (1 hunks)
  • pkg/models/constants.go (1 hunks)
  • pkg/models/network.go (8 hunks)
  • pkg/swagger/docs.go (6 hunks)
  • pkg/swagger/swagger.json (6 hunks)
  • pkg/test/devstack/networking_test.go (1 hunks)
  • webui/lib/api/schema/swagger.json (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: testcontainers-suite / tests
🔇 Additional comments (34)
pkg/compute/port_allocator.go (3)

1-12: Looks good!
All initial declarations and imports are sensible, and the package scoping is appropriate.


175-185: Clean and concise release mechanism.
Releasing ports by removing them from both usedPorts and usedExecutionPorts is straightforward and appears correct.


187-188: Interface compliance confirmed.
The compile-time check reliably ensures portAllocator satisfies PortAllocator.

pkg/test/devstack/networking_test.go (10)

1-2: Build tags are correct.
The test is clearly marked to run in integration or non-unit contexts.


35-67: Robust server setup.
Using a free port and verifying availability via Eventually provides stable test initialization.


69-73: Graceful server teardown confirmed.
The suite properly shuts down the HTTP server in TearDownSuite.


75-95: Scenario configuration approach looks good.
This helper neatly centralizes environment setup for networked jobs.


97-114: Host networking coverage is sufficient.
Skipping on non-Linux avoids platform quirks. The test verifies local host connectivity correctly.


116-131: Bridge networking coverage is solid.
Using host.docker.internal is a standard approach; the test logic is straightforward and verifies the correct connectivity outcome.


133-151: None networking mode test is correct.
Ensuring the job cannot contact the host server aligns with the expected outcome for NetworkNone.


153-189: Dynamic port mapping in Bridge mode is tested well.
The continuous listen and eventual check confirm the exposure on the host port. This approach addresses ephemeral port reuse.


191-228: Host mode port mapping coverage is valid.
Verifying direct host port exposure ensures consistency with the networking design.


230-251: Negative test scenario for privileged port usage.
This effectively ensures that invalid mappings are rejected, covering a key error path.

pkg/models/network.go (10)

15-25: Use of constants is appropriate.
Defining MinimumPort and MaximumPort clarifies port-bound constraints for easy maintenance.


33-35: Renaming NetworkFull to NetworkHost is clear and consistent.
Maintaining a comment about legacy usage helps with backward compatibility.


68-71: New network type recognized.
NetworkBridge clarifies container-level isolation while preserving outbound connectivity.


103-107: New Ports field expands flexibility.
Providing explicit port mappings in NetworkConfig is a valuable enhancement.


125-127: Nil slice handling is correct.
Initializing n.Ports to an empty slice prevents nil-pointer usage later.


141-141: Cloning ports ensures safe copies.
Including Ports: slices.Clone(n.Ports) is consistent with the rest of the copy logic.


151-151: Network type bounds check.
Validating n.Type against known values prevents invalid or out-of-range enumerations.


153-155: HTTP domains check is appropriately enforced.
Restricting domain usage to NetworkHTTP aligns with the design constraints.


169-195: Port mappings validated thoroughly.
Requiring NetworkHost or NetworkBridge for ports is consistent, while also checking duplicates and gating Target usage.


307-380: PortMapping struct is well-defined.
All validation rules make sense, from naming constraints to checking port ranges and IP validity. Good completeness.

pkg/models/constants.go (1)

17-21: LGTM! Well-documented port environment variable prefixes.

The new constants follow the existing naming pattern and are properly documented.

pkg/compute/port_allocator_test.go (1)

23-31: LGTM! Clean test setup.

The test suite setup is clean and follows testing best practices.

cmd/cli/docker/docker_run_cli_test.go (2)

405-405: LGTM! Network type updated correctly.

The test correctly reflects the renaming of 'full' network mode to 'host' as described in the PR objectives.


438-441: LGTM! Correct error handling for invalid network configuration.

The test now correctly expects an error when domains are specified with 'none' network type.

pkg/swagger/docs.go (2)

2030-2050: Well-documented port mapping model!

The PortMapping model is clearly documented with comprehensive descriptions for each field, making it easy for API consumers to understand the port mapping configuration options.


2906-2922: LGTM: Clear network configuration model!

The NetworkConfig model is well-structured with clear documentation for port range configuration and address advertisement.

webui/lib/api/schema/swagger.json (2)

1377-1380: New Compute Node Address Property Added

A new property address has been introduced in models.ComputeNodeInfo to specify the network location of a compute node. This addition improves node discoverability by clearly defining its network identity.


1884-1891: Updated Network Enumeration

The enumeration for models.Network has been updated to include NetworkNone, NetworkHost, NetworkHTTP, and NetworkBridge. This change reflects the renaming of the legacy full mode to host and adds additional network types. Please verify that all documentation and dependent code are updated accordingly to match this new schema.

pkg/swagger/swagger.json (3)

1375-1379: New Property Addition in ComputeNodeInfo.
The new "address" property has been added to models.ComputeNodeInfo to provide the network location (IPv4 or hostname) where the compute node can be reached. Please ensure that API consumers and client libraries are updated accordingly and that any validation (if required) is implemented consistently.


1880-1893: Clarify Network Enumeration Values.
The models.Network enum now lists four values: "NetworkNone", "NetworkHost", "NetworkHTTP", and "NetworkBridge". According to the PR objectives, only three modes (none, host, and bridge) are intended. Please confirm whether "NetworkHTTP" is an additional, intended option or if further documentation/adjustment is required to align the enum with the objectives.


2902-2918: New Types.NetworkConfig Definition.
The new types.NetworkConfig definition (with properties "AdvertisedAddress", "PortRangeEnd", and "PortRangeStart") provides the necessary configuration for compute nodes’ networking. Please verify that the auto-discovery mechanism for the advertised address and the port range allocation logic render correct network behavior in all deployment scenarios.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f90986 and 9a70af8.

📒 Files selected for processing (3)
  • pkg/models/network.go (8 hunks)
  • pkg/models/network_test.go (1 hunks)
  • pkg/test/devstack/networking_test.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: build / Build Binary (windows, amd64)
  • GitHub Check: build / Build Binary (darwin, arm64)
  • GitHub Check: build / Build Binary (darwin, amd64)
  • GitHub Check: build / Build Binary (linux, armv6)
  • GitHub Check: build / Build Binary (linux, armv7)
  • GitHub Check: build / Build Binary (linux, arm64)
  • GitHub Check: build / Build Binary (linux, amd64)
🔇 Additional comments (3)
pkg/test/devstack/networking_test.go (1)

44-46: Consider adding a retry mechanism for ephemeral port conflicts
Even though a free port is obtained, parallel test runs may still cause conflicts. A retry or fallback to a different port upon failure would reduce flakiness in large test environments.

pkg/models/network.go (1)

148-208: Excellent comprehensive validation
Your approach to validating domains, port mappings, and network types is robust, leveraging errors.Join to aggregate multiple errors. This level of detail significantly helps diagnose user configuration issues.

pkg/models/network_test.go (1)

123-317: Great test coverage
The test suite comprehensively covers valid and invalid scenarios for both NetworkConfig and PortMapping, which enhances confidence in the code’s correctness and error handling.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (1)
pkg/models/network.go (1)

147-221: 🛠️ Refactor suggestion

Refactor the Validate method to reduce cyclomatic complexity
The gocyclo report indicates a complexity of 21. Consider extracting domain validation, port mapping checks, etc., into smaller helper functions to simplify this logic.

🧰 Tools
🪛 GitHub Check: lint / go-lint (ubuntu-latest)

[failure] 147-147:
cyclomatic complexity 21 of func (*NetworkConfig).Validate is high (> 18) (gocyclo)

🪛 GitHub Actions: Main Pipeline

[warning] 147-147: cyclomatic complexity 21 of func (*NetworkConfig).Validate is high (> 18) (gocyclo)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70af8 and dce8653.

📒 Files selected for processing (6)
  • pkg/compute/port_allocator.go (1 hunks)
  • pkg/compute/port_allocator_test.go (1 hunks)
  • pkg/models/execution.go (1 hunks)
  • pkg/models/execution_test.go (1 hunks)
  • pkg/models/network.go (8 hunks)
  • pkg/models/network_test.go (1 hunks)
🧰 Additional context used
🪛 GitHub Check: lint / go-lint (ubuntu-latest)
pkg/models/network.go

[failure] 147-147:
cyclomatic complexity 21 of func (*NetworkConfig).Validate is high (> 18) (gocyclo)

🪛 GitHub Actions: Main Pipeline
pkg/models/network.go

[warning] 147-147: cyclomatic complexity 21 of func (*NetworkConfig).Validate is high (> 18) (gocyclo)

🔇 Additional comments (14)
pkg/compute/port_allocator.go (3)

13-23: Add concurrency rationale in struct documentation
It would be helpful to briefly describe how concurrent port allocations are handled, clarifying the mutex usage and potential interactions with other processes that may also bind to ports.


111-129: Revisit time-of-check vs. time-of-use on static port allocation
Using net.Listen and then closing it introduces a window where another external process could claim the port. Consider documenting, or referencing, how the system manages this risk or how frequently it occurs.


130-174: Consider race conditions and fallback for dynamic port allocation
The round-robin approach combined with a quick bind-and-release can collide with external processes. You might consider a persistent bind pattern or OS-allocated ephemeral ports to reduce collisions.

pkg/models/network.go (2)

15-25: Nice introduction of shared port constants
Centralizing numeric port bounds improves clarity and avoids “magic numbers.” Good job.


377-379: Align error message with computed max port name
The error message still says “max 256 characters,” yet maxPortName is 256 - len(EnvVarHostPortPrefix). Update the message to reflect the actual limit.

pkg/models/network_test.go (7)

123-449: Comprehensive test coverage for NetworkConfig_Validate
This set of tests covers a wide range of valid/invalid networking configurations, ensuring the new logic holds up well.


451-603: Thorough tests for PortMapping validation
These scenarios capture the key failure paths (e.g., privileged ports, invalid names), improving reliability.


605-641: DomainSet tests look good
The coverage ensures that distinct and normalized domains behave as expected.


643-645: Use a standard substring check
The current implementation omits the last character and can produce false negatives. Consider using strings.Contains instead.


647-657: Simplicity of equalStringSlices
This helper is straightforward and works well for these test assertions.


659-690: Proper deep copy checks
Verifying that changes to the original config do not affect the copy ensures safe usage.


692-741: Normalization test coverage
Ensuring domains are trimmed, lowercased, and that ports are initialized prevents subtle errors. Good coverage.

pkg/models/execution_test.go (1)

17-208: Consider adding more test cases.

The test suite has good coverage but could benefit from additional test cases:

  1. Test with invalid port numbers (e.g., negative ports, ports > 65535)
  2. Test with duplicate port names
  3. Test with empty port names
  4. Test concurrent port allocation
pkg/models/execution.go (1)

265-278: Add validation for port values.

The method correctly updates port mappings but should validate that:

  1. Port values are within valid range (0-65535)
  2. Port names are not empty
  3. Port names are unique within the network configuration

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

♻️ Duplicate comments (10)
pkg/compute/port_allocator.go (2)

46-109: 🧹 Nitpick (assertive)

Consider fallback for unavailable static ports.

When a static port is unavailable, the current logic returns an error. Some users might prefer a fallback to an ephemeral/dynamic port if the static one can’t be bound.


130-174: 🧹 Nitpick (assertive)

Round-robin logic correctness.

The single pass through the range using round-robin helps distribute allocations, but if the range is nearly exhausted under heavy concurrency, collisions could still occur. Consider additional retry or backoff to manage race conditions more gracefully.

pkg/models/network.go (1)

344-417: 🧹 Nitpick (assertive)

Synchronize error message with maxPortName.

When len(p.Name) > maxPortName, the error message still says “max 256 characters.” Consider using the constant in the formatted string:

- return fmt.Errorf("port name too long (max 256 characters)")
+ return fmt.Errorf("port name too long (max %d characters)", maxPortName)
pkg/models/execution.go (1)

265-277: 🛠️ Refactor suggestion

Add validation for port values.

The method correctly updates port mappings but should validate that:

  1. Port values are within valid range (0-65535)
  2. Port names are not empty
  3. Port names are unique within the network configuration

Apply this diff to add validation:

 func (e *Execution) AllocatePorts(portMap PortMap) {
   if e.Job == nil || e.Job.Task().Network == nil {
     return
   }
 
+  // Validate port mappings
+  seenNames := make(map[string]bool)
+  for _, port := range portMap {
+    if port.Name == "" {
+      return // Skip empty port names
+    }
+    if port.Static < 0 || port.Static > 65535 {
+      return // Skip invalid static ports
+    }
+    if port.Target < 0 || port.Target > 65535 {
+      return // Skip invalid target ports
+    }
+    if seenNames[port.Name] {
+      return // Skip duplicate port names
+    }
+    seenNames[port.Name] = true
+  }
 
   // Replace the existing port mappings with the allocated ones
   if portMap == nil {
     e.Job.Task().Network.Ports = make(PortMap, 0)
   } else {
     e.Job.Task().Network.Ports = portMap.Copy()
   }
 }
pkg/models/network_test.go (1)

677-679: 🛠️ Refactor suggestion

Revise the 'contains' helper.

The current implementation removes the last character and performs multiple extra checks, potentially causing false negatives. Consider using a standard substring approach with strings.Contains.

-func contains(s, substr string) bool {
-    return s != "" && substr != "" && s != substr && s[len(s)-1] != '.' && ...
-}
+func contains(s, substr string) bool {
+    return strings.Contains(s, substr)
+}
pkg/executor/docker/executor_test.go (4)

657-692: 🧹 Nitpick (assertive)

Ensure host mode test is properly guarded for Linux-only execution.

The test correctly uses SkipIfNotLinux as Docker host mode networking is only supported on Linux platforms. However, there are a few suggestions to improve the test:

  1. Add a comment explaining why host mode is Linux-only.
  2. Consider using a more descriptive port name than "http" since this is a test case.

Apply this diff to improve the test:

 func (s *ExecutorTestSuite) TestPortMappingInHostMode() {
+    // Docker host mode networking is only supported on Linux as it requires direct access
+    // to the host's network namespace
     testutils.SkipIfNotLinux(s.T(), "docker host mode is not supported on non-linux platforms")

     port, err := network.GetFreePort()
     s.Require().NoError(err)

     es, err := dockermodels.NewDockerEngineBuilder("busybox:1.37.0").
         WithEntrypoint("sh", "-c", fmt.Sprintf(`while true; do echo -e "HTTP/1.1 200 OK\n\nOK" | nc -l -p %d; done`, port)).
         Build()
     s.Require().NoError(err)

     task := mock.Task()
     task.Engine = es
     task.Network = &models.NetworkConfig{
         Type: models.NetworkHost,
         Ports: []*models.PortMapping{
             {
-                Name:   "http",
+                Name:   "test-port",
                 Static: port,
             },
         },
     }

731-784: 🧹 Nitpick (assertive)

Improve test reliability by using separate test servers.

The test uses a single shell script to start two netcat servers on different ports. This could lead to race conditions or one server failing to start without affecting the other.

Apply this diff to improve test reliability:

-    es, err := dockermodels.NewDockerEngineBuilder("busybox:1.37.0").
-        WithEntrypoint("sh", "-c", `
-            (while true; do echo -e "HTTP/1.1 200 OK\n\nOK" | nc -l -p 80; done) &
-            (while true; do echo -e "HTTP/1.1 200 OK\n\nOK" | nc -l -p 81; done)
-        `).
+    es, err := dockermodels.NewDockerEngineBuilder("busybox:1.37.0").
+        WithEntrypoint("sh", "-c", `
+            # Start first server with proper error handling
+            start_server() {
+                while true; do
+                    echo -e "HTTP/1.1 200 OK\n\nOK" | nc -l -p $1 || {
+                        echo "Server on port $1 failed, retrying..." >&2
+                        sleep 1
+                    }
+                done
+            }
+            start_server 80 &
+            start_server 81 &
+            wait
+        `).
         Build()

786-844: 🛠️ Refactor suggestion

Improve test coverage for host service accessibility.

The test verifies host service access but has a few areas for improvement:

  1. The test server is bound to all interfaces (:0), which could be a security concern.
  2. The test doesn't verify the response body matches expectations.

Apply these changes to improve the test:

     // Start a simple HTTP server on the host
     handler := http.NewServeMux()
     handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-        w.Write([]byte("host-service"))
+        w.Header().Set("Content-Type", "text/plain")
+        w.Write([]byte("host-service-response"))
     })

-    listener, err := net.Listen("tcp", ":0") // Let OS choose port
+    // Bind to localhost for security
+    listener, err := net.Listen("tcp", "127.0.0.1:0")
     s.Require().NoError(err)
     defer listener.Close()

     server := &http.Server{Handler: handler}
     go server.Serve(listener)
     defer server.Close()

     // Get the port that was assigned
     hostPort := listener.Addr().(*net.TCPAddr).Port

     tests := []struct {
         name        string
         networkType models.Network
         hostURL     string
+        expected    string
     }{
         {
             name:        "host network mode",
             networkType: models.NetworkHost,
             hostURL:     fmt.Sprintf("http://localhost:%d", hostPort),
+            expected:    "host-service-response",
         },
         {
             name:        "bridge network mode",
             networkType: models.NetworkBridge,
             hostURL:     fmt.Sprintf("http://host.docker.internal:%d", hostPort),
+            expected:    "host-service-response",
         },
     }

     for _, tt := range tests {
         s.Run(tt.name, func() {
             if tt.networkType == models.NetworkHost {
                 testutils.SkipIfNotLinux(s.T(), "docker host mode is not supported on non-linux platforms")
             }

             // Create a task that tries to connect to our test server
             es, err := dockermodels.NewDockerEngineBuilder(CurlDockerImage).
                 WithEntrypoint("curl", "-s", tt.hostURL).
                 Build()
             s.Require().NoError(err)

             task := mock.Task()
             task.Engine = es
             task.Network = &models.NetworkConfig{
                 Type: tt.networkType,
             }

             stdout, err := s.runJobGetStdout(task, fmt.Sprintf("access-host-%s", tt.networkType))
             s.Require().NoError(err)
-            s.Equal("host-service", stdout)
+            s.Equal(tt.expected, stdout)
         })
     }
🧰 Tools
🪛 ast-grep (0.31.1)

[warning] 792-792: "Detected a network listener listening on 0.0.0.0 or an empty string.
This could unexpectedly expose the server publicly as it binds to all
available interfaces. Instead, specify another IP address that is not
0.0.0.0 nor the empty string."
Context: net.Listen("tcp", ":0")
Note: [CWE-200] Exposure of Sensitive Information to an Unauthorized Actor [REFERENCES]
- https://owasp.org/Top10/A01_2021-Broken_Access_Control

(avoid-bind-to-all-interfaces-go)


694-729: 🧹 Nitpick (assertive)

Add test coverage for dynamic port allocation in bridge mode.

The test verifies port mapping in bridge mode with a static port, but it would be beneficial to also test dynamic port allocation when Static is not specified.

Add a new test case for dynamic port allocation:

func (s *ExecutorTestSuite) TestPortMappingInBridgeModeWithDynamicPort() {
    // Create a container that listens on the target port
    es, err := dockermodels.NewDockerEngineBuilder("busybox:1.37.0").
        WithEntrypoint("sh", "-c", `while true; do echo -e "HTTP/1.1 200 OK\n\nOK" | nc -l -p 80; done`).
        Build()
    s.Require().NoError(err)

    task := mock.Task()
    task.Engine = es
    task.Network = &models.NetworkConfig{
        Type: models.NetworkBridge,
        Ports: []*models.PortMapping{
            {
                Name:   "http",
                Target: 80, // Container port
            },
        },
    }

    // Start the container
    s.startJob(task, "bridge-network-dynamic-port-test")

    // Get the allocated port from environment variables
    // TODO: Add logic to retrieve the dynamically allocated port

    // Verify the port is accessible
    s.Require().Eventually(func() bool {
        resp, err := http.Get(fmt.Sprintf("http://localhost:%d", allocatedPort))
        if err != nil {
            return false
        }
        defer resp.Body.Close()
        return resp.StatusCode == http.StatusOK
    }, 5*time.Second, 100*time.Millisecond)
}
webui/lib/api/schema/swagger.json (1)

2025-2045: 🧹 Nitpick (assertive)

Enhance Port Mapping Schema

The models.Port definition now documents properties for "HostNetwork", "Name", "Static", and "Target". It is recommended to explicitly add a "required": ["Name"] constraint to enforce the mandatory nature of the port mapping identifier. Additionally, consider applying numeric validations (for example, setting "minimum": 1 and "maximum": 65535") for the "Static" and "Target" fields and including examples to improve schema clarity.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dce8653 and 65ba077.

⛔ Files ignored due to path filters (1)
  • webui/lib/api/generated/types.gen.ts is excluded by !**/generated/**
📒 Files selected for processing (14)
  • pkg/compute/envvars_test.go (1 hunks)
  • pkg/compute/port_allocator.go (1 hunks)
  • pkg/compute/port_allocator_test.go (1 hunks)
  • pkg/compute/types.go (1 hunks)
  • pkg/executor/docker/executor_test.go (3 hunks)
  • pkg/models/execution.go (1 hunks)
  • pkg/models/execution_test.go (1 hunks)
  • pkg/models/network.go (8 hunks)
  • pkg/models/network_test.go (3 hunks)
  • pkg/models/task_test.go (5 hunks)
  • pkg/swagger/docs.go (6 hunks)
  • pkg/swagger/swagger.json (6 hunks)
  • pkg/test/devstack/networking_test.go (1 hunks)
  • webui/lib/api/schema/swagger.json (6 hunks)
🧰 Additional context used
🪛 ast-grep (0.31.1)
pkg/executor/docker/executor_test.go

[warning] 792-792: "Detected a network listener listening on 0.0.0.0 or an empty string.
This could unexpectedly expose the server publicly as it binds to all
available interfaces. Instead, specify another IP address that is not
0.0.0.0 nor the empty string."
Context: net.Listen("tcp", ":0")
Note: [CWE-200] Exposure of Sensitive Information to an Unauthorized Actor [REFERENCES]
- https://owasp.org/Top10/A01_2021-Broken_Access_Control

(avoid-bind-to-all-interfaces-go)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: testcontainers-suite / tests
🔇 Additional comments (30)
pkg/compute/port_allocator.go (3)

24-44: Range validation looks good.

The constructor properly checks start < end and adheres to [1024, 65535]. No further issues found here.


111-128: Time-of-check vs time-of-use race condition.

Binding the port via net.Listen at allocation time mitigates some conflicts but still can’t fully guarantee the port remains free by actual container startup. If multiple processes or external services allocate ports concurrently, collisions may arise between allocation and container bind time.


176-186: Release mechanism is clear.

The release logic properly frees ports associated with the execution, which allows reusability. Make sure all call sites consistently invoke ReleasePorts to avoid leaks.

pkg/test/devstack/networking_test.go (5)

35-67: Server initialization approach looks fine.

Using Eventually to verify readiness is a reliable pattern, preventing transient test failures from ephemeral port conflicts.


97-114: Host networking test is well-structured.

The test coverage for host networking is sufficient. No issues found.


116-131: Bridge networking test is straightforward.

The test confirms outbound connectivity via host.docker.internal:; no further concerns.


133-151: Networking-none test is valid.

Verifying that localhost remains inaccessible in none mode helps ensure correct isolation.


230-344: Negative tests are comprehensive.

This covers privileged ports, out-of-range ports, target-port misuse, and duplicate mappings. Great job ensuring multiple invalid configurations lead to job submission failures.

pkg/models/network.go (6)

30-35: Renaming NetworkFull to NetworkHost is clear.

The updated naming helps reduce confusion.


68-71: Bridge mode documentation is helpful.

The introduction of NetworkBridge clarifies the default container networking scenario.


103-106: New Ports field is a useful addition.

Centralizing port mappings under NetworkConfig ensures clarity and easier validation.


124-126: PortMap initialization is consistent.

Normalizing Ports to an empty slice instead of nil prevents potential nil-pointer checks downstream.


146-182: Validation logic for network modes is solid.

Enforcing that ports can only be set for Host or Bridge is consistent with your design.


292-335: PortMap validation is thorough.

Checking for duplicate names, static ports, and host/bridge constraints ensures correct usage.

pkg/compute/types.go (1)

51-64: LGTM! Well-designed interface for port allocation.

The PortAllocator interface is well-structured with clear responsibilities, good documentation, and consideration for thread safety.

pkg/models/execution_test.go (1)

17-208: Add more test cases for edge scenarios.

While the test coverage is good, consider adding the following test cases:

  1. Invalid port numbers (negative ports, ports > 65535)
  2. Duplicate port names
  3. Empty port names
  4. Concurrent port allocation
pkg/compute/port_allocator_test.go (3)

178-196: Enhance port range exhaustion tests.

Add test cases for:

  1. Exhaustion with a mix of static and dynamic ports
  2. Recovery after port release
  3. Concurrent allocation attempts

61-97: Add validation for port values.

The test should validate:

  1. Port values are within valid range (0-65535)
  2. Port names are unique
  3. Host network bindings are preserved

247-283: LGTM! Thorough testing of bridge mode port allocation.

The test case effectively validates:

  • Dynamic port allocation within range
  • Static port preservation
  • Target port defaulting behavior
  • Multiple port configurations
pkg/models/task_test.go (2)

42-42: LGTM! Good defensive programming.

The assertion ensures that port mappings are properly initialized during task normalization, preventing potential nil pointer dereferences.


216-280: LGTM! Comprehensive test coverage for network validation.

The test cases thoroughly validate network configurations including:

  • Network type validation
  • Port mapping name validation
  • Privileged port restrictions
  • Domain configuration rules
pkg/models/network_test.go (1)

485-621: LGTM! Excellent test coverage for port mapping validation.

The test cases thoroughly validate port mappings including:

  • Name validation (empty, invalid chars, length)
  • Port range validation (privileged, max value)
  • Host network validation
  • Edge cases (zero ports, IPv6)
pkg/swagger/docs.go (4)

1883-1897: LGTM! Network type enum updated correctly.

The changes to the Network enum align well with the PR objectives:

  • Added NetworkBridge for bridge mode support
  • Renamed NetworkFull to NetworkHost for better clarity

1898-1917: LGTM! Network configuration updated to support port mapping.

The addition of the Ports field to NetworkConfig enables the port mapping functionality described in the PR objectives.


2029-2049: LGTM! Port mapping model is well-designed.

The Port struct provides a comprehensive model for port mapping with:

  • Required name identifier for environment variables
  • Flexible port allocation with optional static host port
  • Bridge mode support through target container port
  • Network interface binding control

2578-2585: LGTM! Compute node configuration updated for network support.

The addition of the Network field enables compute nodes to specify their network configuration.

webui/lib/api/schema/swagger.json (1)

1880-1893: Verify Updated Network Enumeration

The updated models.Network enumeration now lists four values: "NetworkNone", "NetworkHost", "NetworkHTTP", and "NetworkBridge". Since the PR objectives mention supporting only three modes—none, host, and bridge (with host replacing the legacy "full" mode)—please verify that "NetworkHTTP" is an intended additional mode and not an oversight.

pkg/swagger/swagger.json (3)

1375-1378: New address Field in models.ComputeNodeInfo
The addition of the address property enhances node discovery by allowing the compute node to explicitly advertise its reachable network address. Consider adding validation (for example, a regex pattern) to enforce correct formatting (IPv4 address or hostname) if your validation framework supports schema constraints.


1884-1891: Review Network Mode Enumeration
The updated models.Network enum now includes four values: "NetworkNone", "NetworkHost", "NetworkHTTP", and "NetworkBridge". However, the PR objectives mention only three network modes (bridge, host, and none), with legacy values being renamed. Please verify that the inclusion of "NetworkHTTP" is intentional and, if so, ensure that corresponding documentation and implementation details are updated to reflect this additional mode.


1903-1908: Ports Array in models.NetworkConfig
The new Ports property within models.NetworkConfig correctly references the newly defined models.Port type. Make sure that downstream consumers correctly implement the auto-allocation of ports when the Static property is omitted and default the Target port to the host port in Bridge mode as expected.

wdbaruni and others added 2 commits February 20, 2025 12:56
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65ba077 and 9f6af36.

📒 Files selected for processing (2)
  • pkg/models/network.go (8 hunks)
  • pkg/models/network_test.go (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: testcontainers-suite / tests
🔇 Additional comments (16)
pkg/models/network.go (10)

15-25: Good use of descriptive constants.
Defining MinimumPort, MaximumPort, and maxPortName is a clean approach that replaces magic numbers with clear, maintainable values, aligning with best practices.


30-35: Renaming NetworkFull to NetworkHost is well-handled.
This change clarifies the intended behavior and retains backward compatibility through explicit naming.


68-71: Clear introduction of NetworkBridge.
The added documentation effectively explains the new bridge-based networking mode.


76-82: Legacy “full” fallback
Great job preserving backward compatibility by mapping the legacy "full" value to NetworkHost. Consider documenting a deprecation plan if this is only transitional.


124-126: Consistent Ports initialization.
Ensuring n.Ports is never nil helps avoid panics and keeps the code consistent with the rest of the struct’s normalization behavior.


140-140: Deep copying of Ports.
Referencing n.Ports.Copy() inside the Copy() method is a robust approach to ensure immutability across copies.


146-182: Comprehensive validation for NetworkConfig.
This logical check ensures domains and ports are restricted to appropriate network modes, providing clarity and preventing misconfigurations. No issues spotted.


292-335: Robust PortMap validation.
The checks for duplicate names, static ports, and target ports are well-structured and improve reliability. Good job leveraging errors.Join for aggregated errors.


337-342: Safe Copy for PortMap.
Returning a copied slice ensures changes to one PortMap don't leak into another. Looks good.


344-417: Refine error messages to use shared constants.
Using 65535 directly in errors (e.g., line 400) instead of MaximumPort can lead to inconsistencies if the constant changes. Similarly, consider referencing MinimumPort in error messages for the privileged port range.

pkg/models/network_test.go (6)

9-18: Smooth integration with testify/suite.
Defining a suite struct and running it with suite.Run neatly organizes these tests for maintainability and readability.


20-71: Thorough domain validation tests.
The table-driven approach covers a range of valid and invalid domains, including IP addresses and corner cases like dot-prefixed domains. This well verifies NetworkHTTP domain constraints.


114-152: Effective testing of DomainSet functionality.
Ensuring wildcard and duplicate domain handling is properly tested eliminates potential surprises in filtering.


154-196: Comprehensive domain matching tests.
Verifying comparisons for equality and non-equality across wildcard and case differences ensures robust coverage of matchDomain.


198-228: Deep copy validation.
These tests confirm that changes to the original NetworkConfig do not affect the copy, preventing unintended side effects.


230-279: NetworkConfig normalization tests.
Excellent approach verifying nil inputs, empty slices, and domain trimming. This ensures consistent state handling under a variety of conditions.

Comment on lines +73 to 112
func (s *NetworkTestSuite) TestPortValidation() {
tests := []struct {
name string
port Port
wantErr bool
errMsg string
}{
{
name: "valid port",
port: Port{
Name: "http",
Static: 8080,
},
wantErr: false,
},
{
name: "missing name",
port: Port{
Static: 8080,
},
wantErr: true,
errMsg: "port mapping name is required",
},
// ... other test cases ...
}

for _, tt := range tests {
s.Run(tt.name, func() {
err := tt.port.Validate()
if tt.wantErr {
s.Error(err)
if tt.errMsg != "" {
s.Contains(err.Error(), tt.errMsg)
}
} else {
assert.NoError(t, err)
s.NoError(err)
}
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Port validation test coverage.
The test table adequately checks for required names, verifying typical constraints. Consider adding explicit cases for out-of-range ports and negative port values to confirm error handling.

@wdbaruni wdbaruni merged commit 5de7ab8 into main Feb 20, 2025
14 checks passed
@wdbaruni wdbaruni deleted the eng-624-job-port-mapping branch February 20, 2025 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

1 participant