Skip to content

Conversation

@VietND96
Copy link
Member

@VietND96 VietND96 commented Nov 14, 2025

User description

🔗 Related Issues

💥 What does this PR do?

Add Docker API v1.48 Adapter Support for future-ready if the Minimum API Version gets increased

Docker Engine Version Minimum API Version Maximum API Version Release Date Notes
19.03 - 1.40 Jul 2019 Legacy support baseline
25.0 - 1.44 Jan 2024 VirtualSize removed, multi-network
28.0 - 1.48 Feb 2025 Multi-platform, gateway priority, OCI v1.1 compliance
29.0 1.44 + Nov 2025 Latest, deprecated field removals

API 1.48 https://docs.docker.com/reference/api/engine/version-history/#v148-api-changes
✅ Better multi-architecture support - Deploy Grid nodes across ARM64 and AMD64
✅ Advanced network control - Separate browser and management traffic
✅ Future-proof - Ready for Docker Engine 28.0+ and beyond
✅ Backward compatible - No breaking changes for existing deployments
✅ Enhanced debugging - More detailed image and container metadata

Backward Compatibility
✅ 100% backward compatible with existing v1.44 and v1.40 adapters
✅ All existing Docker client commands work identically
✅ New v1.48 features are optional enhancements
✅ Automatic version detection selects appropriate adapter
✅ No configuration changes required

🔧 Implementation Notes

💡 Additional Considerations

🔄 Types of changes

  • Cleanup (formatting, renaming)
  • Bug fix (backwards compatible)
  • New feature (non-breaking change which adds functionality and tests!)
  • Breaking change (fix or feature that would cause existing functionality to change)

PR Type

Enhancement


Description

  • Add Docker API v1.48 adapter support for future-ready Grid deployments

  • Replace v1.41 with v1.40 as baseline legacy version for backward compatibility

  • Implement multi-platform image support and gateway priority network control

  • Remove hardcoded default API version to enable automatic version detection

  • Simplify Docker client constructors by removing overloaded variants


Diagram Walkthrough

flowchart LR
  A["Docker API Versions"] --> B["v1.40-1.43<br/>V140Adapter"]
  A --> C["v1.44-1.47<br/>V144Adapter"]
  A --> D["v1.48+<br/>V148Adapter"]
  D --> E["Multi-platform<br/>Images"]
  D --> F["Gateway Priority<br/>Networks"]
  D --> G["Image Mount<br/>Type"]
Loading

File Walkthrough

Relevant files
Documentation
3 files
Docker.java
Update API version example documentation                                 
+1/-1     
V140Adapter.java
Update deprecation comment for filter parameter                   
+1/-1     
DockerFlags.java
Update Docker API version documentation and example           
+3/-3     
Enhancement
5 files
VersionCommand.java
Add v1.48 adapter and update version mapping                         
+7/-6     
AdapterFactory.java
Implement v1.48 adapter factory selection logic                   
+11/-4   
DockerClient.java
Remove hardcoded default API version constant                       
+0/-5     
V148Adapter.java
Create new adapter for Docker API v1.48+ features               
+260/-0 
DockerOptions.java
Remove hardcoded default API version constant                       
+1/-2     
Cleanup
8 files
CreateContainer.java
Remove overloaded constructors and simplify initialization
+0/-9     
GetContainerLogs.java
Remove overloaded constructor and default version               
+0/-5     
InspectContainer.java
Remove overloaded constructor variant                                       
+0/-5     
IsContainerPresent.java
Remove overloaded constructor and default version               
+0/-5     
ListImages.java
Remove overloaded constructor and update API docs               
+1/-6     
PullImage.java
Remove overloaded constructor and default version               
+0/-5     
StartContainer.java
Remove overloaded constructor and default version               
+0/-5     
StopContainer.java
Remove overloaded constructor and default version               
+0/-5     
Tests
5 files
BootstrapTest.java
Update test assertion for maximum API version                       
+1/-1     
VersionCommandTest.java
Add comprehensive Docker Engine version compatibility tests
+97/-1   
ListImagesTest.java
Update test method name to reflect v1.40 API version         
+2/-2     
V140AdapterTest.java
Update adapter test to use v1.40 API version                         
+2/-2     
V148AdapterTest.java
Create comprehensive test suite for v1.48 adapter               
+245/-0 

@selenium-ci selenium-ci added B-grid Everything grid and server related C-java Java Bindings labels Nov 14, 2025
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Nov 14, 2025

PR Compliance Guide 🔍

(Compliance updated until commit fe4ab2b)

Below is a summary of compliance checks for this PR:

Security Compliance
Sensitive information exposure

Description: Logging of container mount 'Source' and 'Target' paths at FINE level may unintentionally
expose internal image names or sensitive filesystem paths in logs, which could aid
attackers if logs are accessible.
V148Adapter.java [146-155]

Referred Code
        if (source != null && target != null) {
          LOG.fine(
              String.format(
                  "Mounting image '%s' at '%s' (API v%s+ image mount support)",
                  source, target, apiVersion));
        }
      }
    }
  }
}
Ticket Compliance
🟡
🎫 #5678
🔴 Investigate and fix repeated "Error: ConnectFailure (Connection refused)" when
instantiating multiple ChromeDriver instances on Ubuntu 16.04 with Chrome 65/ChromeDriver
2.35 and Selenium 3.9.0.
Ensure subsequent ChromeDriver instantiations do not fail after a successful first
instance.
Provide a reliable mitigation or code change within Selenium that addresses
Docker/grid-related causes if relevant.
🟡
🎫 #1234
🔴 Clicking a link with javascript in href should trigger JS execution in Selenium
2.48.0/2.48.2 on Firefox 42 like it did in 2.47.1.
Provide compatibility or fix in Firefox driver behavior so alerts from href javascript
fire on click().
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Action Logging: New adapter logic performs significant actions and decisions (e.g., adapting
requests/responses, gateway priority handling) without explicit audit logging beyond
debug/warn, and it is unclear if these are captured in a central audit trail.

Referred Code
@Override
public Map<String, Object> adaptImageResponse(Map<String, Object> response) {
  if (response == null) {
    return response;
  }

  Map<String, Object> adapted = new HashMap<>(response);

  // Ensure VirtualSize is not present (removed in v1.44)
  if (adapted.containsKey("VirtualSize")) {
    LOG.warning(
        "VirtualSize field found in API v"
            + apiVersion
            + " response. This field was removed in v1.44.");
    adapted.remove("VirtualSize");
  }

  // Ensure Size field is present (required in v1.44+)
  if (!adapted.containsKey("Size")) {
    LOG.warning("Size field missing from image response in API v" + apiVersion);
  }


 ... (clipped 135 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Edge Cases: Version selection adds a new >=1.48 branch but lacks explicit handling for malformed
versions or unsupported ranges beyond IllegalArgumentException on null/empty, relying on
compareVersions behavior not shown in diff.

Referred Code
// API v1.48+ uses the latest adapter with multi-platform and gateway priority support
if (compareVersions(apiVersion, "1.48") >= 0) {
  LOG.fine("Using V148Adapter for API version " + apiVersion);
  return new V148Adapter(apiVersion);
}

// API v1.44-1.47 uses the v1.44 adapter
if (compareVersions(apiVersion, "1.44") >= 0) {
  LOG.fine("Using V144Adapter for API version " + apiVersion);
  return new V144Adapter(apiVersion);
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Input Validation: The adapter trusts map structures from Docker responses/requests and logs derived values
without validating types/contents beyond instanceof checks, which may require additional
validation depending on upstream trust boundaries.

Referred Code
@Override
public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> request) {
  if (request == null) {
    return request;
  }

  Map<String, Object> adapted = new HashMap<>(request);

  // v1.48+ supports Mount type "image" for mounting images inside containers
  // Validate image mount configurations
  @SuppressWarnings("unchecked")
  Map<String, Object> hostConfig = (Map<String, Object>) adapted.get("HostConfig");

  if (hostConfig != null) {
    @SuppressWarnings("unchecked")
    Object mounts = hostConfig.get("Mounts");

    if (mounts instanceof Iterable) {
      for (Object mount : (Iterable<?>) mounts) {
        if (mount instanceof Map) {
          @SuppressWarnings("unchecked")


 ... (clipped 75 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit ca10286
Security Compliance
Untrusted API data handling

Description: Removing fields from untrusted Docker API responses (e.g., "VirtualSize") alters data
semantics but does not validate or sanitize other fields; if downstream logic assumes
sanitized content, crafted responses could bypass checks—verify consumers do not rely on
presence/absence of these fields for security decisions.
V148Adapter.java [56-79]

Referred Code
Map<String, Object> adapted = new HashMap<>(response);

// v1.48+ includes ImageManifestDescriptor for multi-platform images
if (adapted.containsKey("ImageManifestDescriptor")) {
  LOG.fine(
      "Image response includes ImageManifestDescriptor (multi-platform support in API v"
          + apiVersion
          + ")");
}

// v1.48+ includes Descriptor field (OCI descriptor)
if (adapted.containsKey("Descriptor")) {
  LOG.fine("Image response includes OCI Descriptor field (API v" + apiVersion + ")");
}

// Ensure VirtualSize is not present (removed in v1.44)
if (adapted.containsKey("VirtualSize")) {
  LOG.warning(
      "VirtualSize field found in API v"
          + apiVersion
          + " response. This field was removed in v1.44.");


 ... (clipped 3 lines)
Ticket Compliance
🟡
🎫 #5678
🔴 Investigate and resolve repeated "Error: ConnectFailure (Connection refused)" when
instantiating ChromeDriver multiple times on Ubuntu 16.04.4 with Chrome 65/ChromeDriver
2.35, Selenium 3.9.0.
Ensure subsequent ChromeDriver instantiations do not fail while first succeeds.
Provide a fix or actionable guidance specific to the environment described.
🟡
🎫 #1234
🔴 Ensure WebDriver click() triggers JavaScript in link href on Firefox 42.0 where it
regressed in 2.48.0/2.48.2 compared to 2.47.1.
Provide tests confirming click() behavior executes javascript: links.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Lacking Audit: New adapter logic performs significant protocol adaptations and warnings but does not emit
any structured audit logs of critical actions or outcomes, making it unclear if critical
operations are traceable.

Referred Code
private static final Logger LOG = Logger.getLogger(V148Adapter.class.getName());
private final String apiVersion;

public V148Adapter(String apiVersion) {
  this.apiVersion = apiVersion;
}

@Override
public Map<String, Object> adaptImageResponse(Map<String, Object> response) {
  if (response == null) {
    return response;
  }

  Map<String, Object> adapted = new HashMap<>(response);

  // v1.48+ includes ImageManifestDescriptor for multi-platform images
  if (adapted.containsKey("ImageManifestDescriptor")) {
    LOG.fine(
        "Image response includes ImageManifestDescriptor (multi-platform support in API v"
            + apiVersion
            + ")");


 ... (clipped 87 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Error Handling: Version selection adds new branches for API >=1.48 without explicit handling for
unknown/unsupported versions beyond logging, which may rely on external callers for
graceful degradation.

Referred Code
// API v1.48+ uses the latest adapter with multi-platform and gateway priority support
if (compareVersions(apiVersion, "1.48") >= 0) {
  LOG.fine("Using V148Adapter for API version " + apiVersion);
  return new V148Adapter(apiVersion);
}

// API v1.44-1.47 uses the v1.44 adapter
if (compareVersions(apiVersion, "1.44") >= 0) {
  LOG.fine("Using V144Adapter for API version " + apiVersion);
  return new V144Adapter(apiVersion);

Learn more about managing compliance generic rules or creating your own custom rules

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Nov 14, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Implement new Docker API features

The PR adds support for Docker API v1.48 but fails to implement its new
features. The V148Adapter only logs the presence of new fields rather than
utilizing them, so the code should be updated to leverage these capabilities.

Examples:

java/src/org/openqa/selenium/docker/client/V148Adapter.java [83-146]
  @Override
  public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> request) {
    if (request == null) {
      return request;
    }

    Map<String, Object> adapted = new HashMap<>(request);

    // v1.48+ supports Mount type "image" for mounting images inside containers
    @SuppressWarnings("unchecked")

 ... (clipped 54 lines)

Solution Walkthrough:

Before:

// java/src/org/openqa/selenium/docker/client/V148Adapter.java
class V148Adapter implements ApiVersionAdapter {
  // ...
  @Override
  public Map<String, Object> adaptContainerCreateRequest(Map<String, Object> request) {
    // ...
    // This block only logs the presence of 'GwPriority' but doesn't add it.
    // The calling code doesn't provide it in the first place.
    if (endpointConfig.containsKey("GwPriority")) {
      LOG.fine(
          "Network endpoint '"
              + entry.getKey()
              + "' includes GwPriority (supported in API v"
              + apiVersion
              + "+)");
    }
    // ...
    return adapted; // Returns the request map, possibly with minor adaptations.
  }
}

After:

// A conceptual change, as it requires modifications in multiple classes.
// 1. Update ContainerConfig to include new options.
class ContainerConfig {
  // ...
  private Optional<String> platform = Optional.empty();
  // ... with getters/setters
}

// 2. Update calling code to use the new options.
// In a class like DockerOptions or similar...
public Container create(ContainerConfig config) {
  // The 'platform' would need to be passed down.
  // For image pulling, the platform parameter should be added to the request.
  // For container creation, the config would already contain the necessary fields.
  // The adapter would then ensure they are correctly formatted.
  // e.g., in PullImage.java
  pullImage.apply(reference, config.getPlatform());
}
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical gap where the PR's core advertised features, like multi-platform support, are not functionally implemented, making the V148Adapter largely a placeholder.

High
Possible issue
Avoid modifying nested map directly
Suggestion Impact:The commit introduced an originalNetworkSettings reference, created a new HashMap copy, and replaced the entry in adapted before processing, thereby avoiding mutation of the original nested map.

code diff:

     // v1.48+ includes GwPriority in NetworkSettings
+    // Identify which network is providing the default gateway
     @SuppressWarnings("unchecked")
-    Map<String, Object> networkSettings = (Map<String, Object>) adapted.get("NetworkSettings");
-
-    if (networkSettings != null) {
+    Map<String, Object> originalNetworkSettings =
+        (Map<String, Object>) adapted.get("NetworkSettings");
+
+    if (originalNetworkSettings != null) {
+      // Create defensive copy to avoid mutating the original response
+      Map<String, Object> networkSettings = new HashMap<>(originalNetworkSettings);
+      adapted.put("NetworkSettings", networkSettings);
+
       @SuppressWarnings("unchecked")

To avoid side effects, create a defensive copy of the networkSettings map in
adaptContainerInspectResponse before modifying it, ensuring the original
response data structure remains unchanged.

java/src/org/openqa/selenium/docker/client/V148Adapter.java [165-205]

 @SuppressWarnings("unchecked")
-Map<String, Object> networkSettings = (Map<String, Object>) adapted.get("NetworkSettings");
+Map<String, Object> originalNetworkSettings =
+    (Map<String, Object>) adapted.get("NetworkSettings");
 
-if (networkSettings != null) {
+if (originalNetworkSettings != null) {
+  // Create a mutable copy to avoid modifying the original map
+  Map<String, Object> networkSettings = new HashMap<>(originalNetworkSettings);
+
   @SuppressWarnings("unchecked")
   Map<String, Object> networks = (Map<String, Object>) networkSettings.get("Networks");
 
   if (networks != null) {
     for (Map.Entry<String, Object> entry : networks.entrySet()) {
       if (entry.getValue() instanceof Map) {
         @SuppressWarnings("unchecked")
         Map<String, Object> network = (Map<String, Object>) entry.getValue();
 
         if (network.containsKey("GwPriority")) {
           LOG.fine(
               "Network '" + entry.getKey() + "' includes GwPriority (API v" + apiVersion + ")");
         }
       }
     }
   }
 
   // Remove deprecated fields (should not be present in v1.48+)
   String[] deprecatedFields = {
     "HairpinMode",
     "LinkLocalIPv6Address",
     "LinkLocalIPv6PrefixLen",
     "SecondaryIPAddresses",
     "SecondaryIPv6Addresses",
     "Bridge" // Deprecated in v1.51, removed in v1.52
   };
 
   for (String field : deprecatedFields) {
     if (networkSettings.containsKey(field)) {
       LOG.fine(
           "Removing deprecated field '"
               + field
               + "' from NetworkSettings (deprecated in earlier API versions)");
       networkSettings.remove(field);
     }
   }
+  adapted.put("NetworkSettings", networkSettings);
 }

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that a nested map is being modified, which could lead to side effects. Creating a defensive copy is good practice for immutability and prevents unintended modification of the original data structure.

Low
Learned
best practice
Validate API version format

Validate the apiVersion format before parsing and clamp/handle unexpected
formats to avoid NumberFormatException and ambiguous errors.

java/src/org/openqa/selenium/docker/client/AdapterFactory.java [47-67]

 public static ApiVersionAdapter createAdapter(String apiVersion) {
   if (apiVersion == null || apiVersion.trim().isEmpty()) {
     throw new IllegalArgumentException("API version cannot be null or empty");
   }
-
-  // API v1.48+ uses the latest adapter with multi-platform and gateway priority support
-  if (compareVersions(apiVersion, "1.48") >= 0) {
-    LOG.fine("Using V148Adapter for API version " + apiVersion);
-    return new V148Adapter(apiVersion);
+  String ver = apiVersion.trim();
+  if (!ver.matches("\\d+\\.\\d+")) {
+    throw new IllegalArgumentException("Invalid Docker API version format: " + apiVersion + " (expected 'major.minor', e.g., '1.44')");
   }
 
-  // API v1.44-1.47 uses the v1.44 adapter
-  if (compareVersions(apiVersion, "1.44") >= 0) {
-    LOG.fine("Using V144Adapter for API version " + apiVersion);
-    return new V144Adapter(apiVersion);
+  if (compareVersions(ver, "1.48") >= 0) {
+    LOG.fine("Using V148Adapter for API version " + ver);
+    return new V148Adapter(ver);
   }
-
-  // API v1.40-1.43 uses the legacy adapter
-  LOG.fine("Using V140Adapter for API version " + apiVersion);
-  return new V140Adapter(apiVersion);
+  if (compareVersions(ver, "1.44") >= 0) {
+    LOG.fine("Using V144Adapter for API version " + ver);
+    return new V144Adapter(ver);
+  }
+  LOG.fine("Using V140Adapter for API version " + ver);
+  return new V140Adapter(ver);
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Guard external API and I/O dependent inputs with targeted validation and clear errors.

Low
  • Update

Signed-off-by: Viet Nguyen Duc <[email protected]>
@VietND96 VietND96 merged commit bc5d050 into trunk Nov 14, 2025
46 checks passed
@VietND96 VietND96 deleted the dynamic-grid branch November 14, 2025 23:42
This was referenced Dec 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-grid Everything grid and server related C-java Java Bindings Review effort 3/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants