Skip to content

Commit d5202c1

Browse files
committed
Fixed the Docker download for Linux and Mac
1 parent 8d1110c commit d5202c1

3 files changed

Lines changed: 8 additions & 58 deletions

File tree

src/main/java/io/github/intisy/docker/DockerVersionFetcher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public static String getLatestVersion() {
3737

3838
try (InputStreamReader reader = new InputStreamReader(conn.getInputStream())) {
3939
JsonObject jsonObject = new Gson().fromJson(reader, JsonObject.class);
40-
latestVersion = jsonObject.get("tag_name").getAsString().replace("v", "");
40+
String tagName = jsonObject.get("tag_name").getAsString();
41+
latestVersion = tagName.replace("v", "").replace("docker-", "");
4142
log.debug("Latest Docker version: {}", latestVersion);
4243
}
4344
} catch (IOException e) {

src/main/java/io/github/intisy/docker/IOUtils.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
*/
1212
final class IOUtils {
1313

14-
private IOUtils() {
15-
// Utility class
16-
}
14+
private IOUtils() {}
1715

1816
/**
1917
* Reads all bytes from an input stream (Java 8 compatible replacement for InputStream.readAllBytes()).

src/main/java/io/github/intisy/docker/WindowsDockerProvider.java

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
public class WindowsDockerProvider extends DockerProvider {
2828
private static final Logger log = LoggerFactory.getLogger(WindowsDockerProvider.class);
2929

30-
private static final String DOCKER_DOWNLOAD_URL = "https://download.docker.com/win/static/stable/%s/%s.zip";
30+
private static final String DOCKER_DOWNLOAD_URL = "https://download.docker.com/win/static/stable/%s/docker-%s.zip";
3131
private static final Path DOCKER_PATH = DOCKER_DIR.resolve("docker/dockerd.exe");
3232
private static final Path DOCKER_VERSION_FILE = DOCKER_DIR.resolve(".docker-version");
3333

@@ -107,7 +107,6 @@ private void ensureWsl2DockerInstalled() throws IOException {
107107
log.info("Checking Docker installation in WSL2 (distro: {})...", wslDistro);
108108

109109
try {
110-
// Check if dockerd exists
111110
ProcessBuilder checkPb = new ProcessBuilder("wsl", "-d", wslDistro, "-e", "bash", "-c", "command -v dockerd");
112111
checkPb.redirectErrorStream(true);
113112
Process checkProcess = checkPb.start();
@@ -241,19 +240,15 @@ private void startNativeDocker() throws IOException, InterruptedException {
241240
log.info("Docker daemon started (instance: {}, pipe: {})", instanceId, pipeName);
242241
}
243242

244-
// Port for this instance's Docker daemon (base port + hash of instance ID)
245243
private int dockerPort;
246244

247245
private void startWsl2Docker() throws IOException, InterruptedException {
248246
ensureInstalled();
249247

250-
// Use TCP instead of Unix socket so Windows Java can connect
251-
// Generate a unique port based on instance ID to avoid conflicts
252248
dockerPort = 2375 + Math.abs(instanceId.hashCode() % 1000);
253-
wslSocketPath = "tcp://0.0.0.0:" + dockerPort; // Listen on all interfaces inside WSL
249+
wslSocketPath = "tcp://0.0.0.0:" + dockerPort;
254250
String wslLogFile = "/tmp/docker-java-" + instanceId + ".log";
255251

256-
// Get the actual home directory path in WSL
257252
String wslHome = runWslCommand("echo $HOME", false, 5);
258253
if (wslHome.isEmpty()) {
259254
wslHome = "/home/" + runWslCommand("whoami", false, 5);
@@ -264,24 +259,19 @@ private void startWsl2Docker() throws IOException, InterruptedException {
264259
String wslExecDir = wslHome + "/.docker-java/instances/" + instanceId + "/exec";
265260
String wslPidFile = wslHome + "/.docker-java/instances/" + instanceId + "/docker.pid";
266261

267-
// Create directories in WSL2
268262
log.debug("Creating directories in WSL2 (distro: {})...", wslDistro);
269263
String mkdirResult = runWslCommand("mkdir -p " + wslDataDir + " " + wslExecDir + " && echo ok", false, 10);
270264
log.debug("mkdir result: {}", mkdirResult);
271265

272-
// Remove any existing socket
273266
runWslCommand("rm -f " + wslSocketPath, false, 5);
274267

275-
// Check if Docker Desktop daemon is running and might conflict
276268
String existingDocker = runWslCommand("pgrep -f 'dockerd' 2>/dev/null", false, 5);
277269
if (!existingDocker.isEmpty()) {
278270
log.warn("Another dockerd process may be running (PIDs: {}). This might cause conflicts.", existingDocker.replace("\n", ", "));
279271
}
280272

281273
log.info("Starting Docker daemon in WSL2 (instance: {}, distro: {})...", instanceId, wslDistro);
282274

283-
// dockerd always needs root to start (docker group only helps with client access)
284-
// Check if passwordless sudo is configured
285275
if (!checkPasswordlessSudo()) {
286276
log.error("Starting dockerd requires root privileges.");
287277
log.error("Please run this ONE-TIME setup in WSL ({}):", wslDistro);
@@ -296,36 +286,28 @@ private void startWsl2Docker() throws IOException, InterruptedException {
296286

297287
log.debug("Passwordless sudo for dockerd is available");
298288

299-
// Check if another dockerd is running (like Docker Desktop)
300289
String existingDockerds = runWslCommand("pgrep -x dockerd 2>/dev/null | wc -l", false, 5).trim();
301290
boolean otherDockerdRunning = !"0".equals(existingDockerds) && !existingDockerds.isEmpty();
302291

303-
// Determine isolation flags to avoid conflicts with other Docker daemons
304292
String isolationFlags = "";
305293
if (otherDockerdRunning) {
306294
log.info("Another Docker daemon detected, using isolation flags to avoid conflicts");
307-
// Use --iptables=false to prevent conflicts with existing Docker's iptables rules
308-
// Use --bridge=none to disable default bridge (avoids need to create custom bridge device)
309295
isolationFlags = " --iptables=false --bridge=none";
310296
}
311297

312298
log.debug("Starting dockerd directly...");
313299

314-
// Build the dockerd command - use TCP so Windows Java can connect
315300
String dockerdCmd = String.format(
316301
"sudo dockerd -H %s --data-root %s --exec-root %s --pidfile %s%s",
317302
wslSocketPath, wslDataDir, wslExecDir, wslPidFile, isolationFlags);
318303

319304
log.debug("Docker command: {}", dockerdCmd);
320305

321-
// First, test if the command works by running dockerd --version
322306
String versionCheck = runWslCommand("sudo dockerd --version 2>&1", false, 10);
323307
log.debug("dockerd version check: {}", versionCheck);
324308

325-
// Clear any old log file
326309
runWslCommand("rm -f " + wslLogFile, false, 5);
327310

328-
// Start dockerd directly, capturing output
329311
log.debug("Executing dockerd command...");
330312
ProcessBuilder pb = new ProcessBuilder(
331313
"wsl", "-d", wslDistro, "--", "bash", "-c",
@@ -334,74 +316,60 @@ private void startWsl2Docker() throws IOException, InterruptedException {
334316
);
335317
pb.redirectErrorStream(true);
336318

337-
// Start the process
338319
dockerProcess = pb.start();
339320

340-
// Give it time to initialize (Docker 29+ has a deliberate 1s+ delay for security warnings)
341321
Thread.sleep(8000);
342322

343-
// Check if process is still running
344323
if (!dockerProcess.isAlive()) {
345324
byte[] output = readAllBytes(dockerProcess.getInputStream());
346325
String outputStr = new String(output).trim();
347326
log.error("WSL process died. Output: {}", outputStr.isEmpty() ? "(empty)" : outputStr);
348327

349-
// Check log file
350328
String logContent = runWslCommand("cat " + wslLogFile + " 2>/dev/null || echo '(no log)'", false, 5);
351329
log.error("Log file contents:\n{}", logContent);
352330

353331
throw new RuntimeException("dockerd failed to start. Check logs above.");
354332
}
355333

356-
// Check if dockerd is actually running inside WSL
357334
String dockerdCheck = runWslCommand("pgrep -x dockerd && echo 'running' || echo 'not running'", false, 5);
358335
log.debug("dockerd process check: {}", dockerdCheck.trim());
359336

360-
// Check log file for any startup messages
361337
String earlyLog = runWslCommand("cat " + wslLogFile + " 2>/dev/null | head -20", false, 5);
362338
if (!earlyLog.isEmpty()) {
363339
log.debug("Early log output:\n{}", earlyLog);
364340
}
365341

366-
// Check if our specific dockerd is running (by port)
367342
String ourDockerd = runWslCommand("pgrep -af 'dockerd.*" + dockerPort + "' 2>/dev/null || echo 'not found'", false, 5);
368343
log.debug("Our dockerd process (by port {}): {}", dockerPort, ourDockerd.trim());
369344

370345
log.debug("WSL process is alive, waiting for port {}...", dockerPort);
371346

372347
if (!waitForWslSocket()) {
373-
// Additional diagnostics when connection fails
374348
String portCheck = runWslCommand("ss -tlnp 2>/dev/null | grep " + dockerPort + " || echo 'port not found'", false, 5);
375349
log.error("Port {} status after wait: {}", dockerPort, portCheck.trim());
376-
// Try to get error log
377350
String logContent = runWslCommand("cat " + wslLogFile + " 2>/dev/null", false, 5);
378351
if (!logContent.isEmpty()) {
379352
log.error("Docker daemon log:\n{}", logContent);
380353
} else {
381354
log.error("Docker daemon log is empty.");
382355
}
383356

384-
// Check if port is listening INSIDE WSL
385357
String netstatCheck = runWslCommand("ss -tlnp 2>/dev/null | grep " + dockerPort + " || echo 'port not listening in WSL'", false, 5);
386358
log.error("Port {} status inside WSL: {}", dockerPort, netstatCheck.trim());
387359

388-
// Check if dockerd is running inside WSL
389360
String dockerdPs = runWslCommand("ps aux | grep dockerd | grep -v grep || echo 'no dockerd process'", false, 5);
390361
log.error("dockerd processes in WSL:\n{}", dockerdPs);
391362

392-
// Check if our dockerd process exists (look for our port)
393363
String psOutput = runWslCommand("pgrep -af 'dockerd.*" + dockerPort + "' 2>/dev/null || echo '(none with our port)'", false, 5);
394364
log.error("Our dockerd process: {}", psOutput);
395365

396-
// Check if Docker Desktop's dockerd is blocking
397366
String allDockerds = runWslCommand("pgrep -af dockerd 2>/dev/null || echo '(none)'", false, 5);
398367
if (allDockerds.contains("-H fd://")) {
399368
log.error("");
400369
log.error("Docker Desktop's dockerd is running in WSL2.");
401370
log.error("Stop it with: wsl -d {} -- sudo systemctl stop docker", wslDistro);
402371
}
403372

404-
// Check if our WSL process is still running
405373
if (dockerProcess != null && dockerProcess.isAlive()) {
406374
log.error("WSL process is still running but port not accessible from Windows");
407375
log.error("This might be a WSL2 networking issue. Try: wsl --shutdown");
@@ -419,9 +387,7 @@ private void startWsl2Docker() throws IOException, InterruptedException {
419387
*/
420388
private boolean checkPasswordlessSudo() {
421389
try {
422-
// Try sudo -n (non-interactive) to check if dockerd can be run without password
423-
// We check with 'sudo -n dockerd --version' to verify dockerd specifically
424-
ProcessBuilder pb = new ProcessBuilder("wsl", "-d", wslDistro, "-e", "bash", "-c",
390+
ProcessBuilder pb = new ProcessBuilder("wsl", "-d", wslDistro, "-e", "bash", "-c",
425391
"sudo -n dockerd --version >/dev/null 2>&1 && echo yes || echo no");
426392
pb.redirectErrorStream(true);
427393
Process process = pb.start();
@@ -506,12 +472,10 @@ private boolean isAdministrator() {
506472

507473
private boolean isWsl2Available() {
508474
try {
509-
// First check: try wsl -l -v which lists distros with their WSL version
510475
ProcessBuilder pb = new ProcessBuilder("wsl", "-l", "-v");
511476
pb.redirectErrorStream(true);
512477
Process process = pb.start();
513478

514-
// Read as bytes and convert, handling potential UTF-16 encoding
515479
byte[] outputBytes = readAllBytes(process.getInputStream());
516480
String output = new String(outputBytes, java.nio.charset.StandardCharsets.UTF_16LE);
517481

@@ -523,7 +487,6 @@ private boolean isWsl2Available() {
523487
return false;
524488
}
525489

526-
// Parse the output to find a usable distro (not docker-desktop or docker-desktop-data)
527490
String[] lines = output.split("\n");
528491
String defaultDistro = null;
529492
String usableDistro = null;
@@ -534,15 +497,13 @@ private boolean isWsl2Available() {
534497
continue;
535498
}
536499

537-
// Check for WSL2 distro (has "2" in version column)
538500
if (!line.contains("2")) {
539501
continue;
540502
}
541503

542504
boolean isDefault = line.startsWith("*");
543505
String distroName = line.replace("*", "").trim().split("\\s+")[0];
544506

545-
// Skip Docker Desktop's internal distros
546507
if (distroName.toLowerCase().startsWith("docker-desktop")) {
547508
log.debug("Skipping Docker Desktop distro: {}", distroName);
548509
if (isDefault) {
@@ -551,7 +512,6 @@ private boolean isWsl2Available() {
551512
continue;
552513
}
553514

554-
// Found a usable distro
555515
if (usableDistro == null || isDefault) {
556516
usableDistro = distroName;
557517
log.debug("Found usable WSL2 distro: {} (default: {})", distroName, isDefault);
@@ -562,7 +522,6 @@ private boolean isWsl2Available() {
562522
wslDistro = usableDistro;
563523
log.info("Using WSL2 distro: {}", wslDistro);
564524

565-
// Verify the distro has bash
566525
ProcessBuilder testPb = new ProcessBuilder("wsl", "-d", wslDistro, "-e", "bash", "-c", "echo test");
567526
testPb.redirectErrorStream(true);
568527
Process testProcess = testPb.start();
@@ -577,7 +536,6 @@ private boolean isWsl2Available() {
577536
}
578537
}
579538

580-
// No usable distro found
581539
if (defaultDistro != null && defaultDistro.toLowerCase().startsWith("docker-desktop")) {
582540
log.warn("Only Docker Desktop WSL distros found. These cannot be used to run dockerd.");
583541
log.warn("Install a Linux distro with: wsl --install -d Ubuntu");
@@ -596,7 +554,6 @@ private boolean isWsl2Available() {
596554
public DockerClient getClient() {
597555
if (this.dockerClient == null) {
598556
if (usingWsl2) {
599-
// Connect via TCP to WSL2
600557
String host = wslIpAddress != null ? wslIpAddress : "localhost";
601558
this.dockerClient = DockerClient.builder()
602559
.withHost("tcp://" + host + ":" + dockerPort)
@@ -617,7 +574,6 @@ public void stop() {
617574
if (dockerProcess != null) {
618575
if (usingWsl2 && wslDistro != null) {
619576
try {
620-
// Kill dockerd process by port
621577
ProcessBuilder pb = new ProcessBuilder("wsl", "-d", wslDistro, "-e", "bash", "-c",
622578
"sudo -n pkill -f 'dockerd.*" + dockerPort + "' 2>/dev/null ; " +
623579
"rm -rf ~/.docker-java/instances/" + instanceId);
@@ -670,7 +626,6 @@ private boolean waitForPipe() throws InterruptedException {
670626

671627
@SuppressWarnings("BusyWait")
672628
private boolean waitForWslSocket() throws InterruptedException {
673-
// Get WSL2's IP address
674629
wslIpAddress = runWslCommand("hostname -I | awk '{print $1}'", false, 5).trim();
675630
if (wslIpAddress.isEmpty()) {
676631
wslIpAddress = "localhost";
@@ -682,8 +637,7 @@ private boolean waitForWslSocket() throws InterruptedException {
682637
long startTime = System.currentTimeMillis();
683638
int attempts = 0;
684639

685-
// Try both localhost and WSL IP
686-
String[] hosts = wslIpAddress.equals("localhost") ?
640+
String[] hosts = wslIpAddress.equals("localhost") ?
687641
new String[]{"localhost"} :
688642
new String[]{"localhost", wslIpAddress};
689643

@@ -695,20 +649,17 @@ private boolean waitForWslSocket() throws InterruptedException {
695649
java.net.Socket socket = new java.net.Socket();
696650
socket.connect(new java.net.InetSocketAddress(host, dockerPort), 1000);
697651
socket.close();
698-
wslIpAddress = host; // Remember which one worked
652+
wslIpAddress = host;
699653
log.debug("Docker daemon is listening on {}:{} after {} attempts", host, dockerPort, attempts);
700654

701-
// Verify our dockerd is actually running
702655
String verify = runWslCommand("ss -tlnp 2>/dev/null | grep " + dockerPort, false, 5);
703656
log.debug("Port {} listener info: {}", dockerPort, verify.trim());
704657

705658
return true;
706659
} catch (IOException e) {
707-
// Not ready yet on this host
708660
}
709661
}
710662

711-
// Log progress every 10 seconds
712663
if (attempts % 20 == 0) {
713664
log.debug("Still waiting for Docker daemon... ({} seconds elapsed)", (System.currentTimeMillis() - startTime) / 1000);
714665
}

0 commit comments

Comments
 (0)