Skip to content

Commit 82c2210

Browse files
committed
Validate volume mounts and create zot config
Add pre-run validation for bind mounts and ensure proper zot config handling. - Introduce EnsureVolumeMountSource in FallbackDockerEngine and LocalDockerClient and call it before creating containers to prevent Docker from creating directories when files are expected. The helper ensures parent directories exist, removes incorrectly-created directories for file mounts, creates missing directories, and throws a FileNotFoundException if a required file mount is missing. - In DockerHubClient, ensure ConfigMounts directory exists, remove an incorrectly-created directory at the config path, and attempt to copy a zot-config.json from the app base directory or create a default zot-config.json if none is found. Console messages were added for visibility.
1 parent 3596284 commit 82c2210

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

cli/src/Vdk/Services/DockerHubClient.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,67 @@ public void CreateRegistry()
1515
var configFile = new FileInfo(Path.Combine("ConfigMounts", "zot-config.json"));
1616
var imagesDir = new DirectoryInfo("images");
1717

18+
// Ensure ConfigMounts directory exists
19+
var configMountsDir = configFile.Directory;
20+
if (configMountsDir != null && !configMountsDir.Exists)
21+
{
22+
configMountsDir.Create();
23+
}
24+
25+
// Fix: Check if config file was incorrectly created as a directory by Docker
26+
if (Directory.Exists(configFile.FullName))
27+
{
28+
console.WriteLine($"Config path '{configFile.FullName}' exists as a directory instead of a file. Removing...");
29+
Directory.Delete(configFile.FullName, recursive: true);
30+
}
31+
32+
// Ensure config file exists - try to copy from app directory or create default
33+
if (!configFile.Exists)
34+
{
35+
// Try to find config in application base directory
36+
var appBaseConfig = Path.Combine(AppContext.BaseDirectory, "ConfigMounts", "zot-config.json");
37+
if (File.Exists(appBaseConfig))
38+
{
39+
console.WriteLine($"Copying zot config from {appBaseConfig}");
40+
File.Copy(appBaseConfig, configFile.FullName);
41+
}
42+
else
43+
{
44+
// Create default config
45+
console.WriteLine("Creating default zot-config.json");
46+
var defaultConfig = """
47+
{
48+
"distSpecVersion": "1.1.0",
49+
"storage": {
50+
"rootDirectory": "/var/lib/registry",
51+
"gc": true,
52+
"gcDelay": "1h",
53+
"gcInterval": "24h"
54+
},
55+
"http": {
56+
"address": "0.0.0.0",
57+
"port": "5000"
58+
},
59+
"log": {
60+
"level": "info"
61+
},
62+
"extensions": {
63+
"ui": {
64+
"enable": true
65+
},
66+
"search": {
67+
"enable": true,
68+
"cve": {
69+
"updateInterval": "24h"
70+
}
71+
}
72+
}
73+
}
74+
""";
75+
File.WriteAllText(configFile.FullName, defaultConfig);
76+
}
77+
}
78+
1879
// Ensure images directory exists
1980
if (!imagesDir.Exists)
2081
{

cli/src/Vdk/Services/FallbackDockerEngine.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ internal static bool RunProcess(string fileName, string arguments, out string st
2525

2626
public bool Run(string image, string name, PortMapping[]? ports, Dictionary<string, string>? envs, FileMapping[]? volumes, string[]? commands, string? network = null)
2727
{
28+
// Validate and fix volume mount sources before creating container
29+
// This prevents Docker from creating directories when files are expected
30+
if (volumes != null)
31+
{
32+
foreach (var volume in volumes)
33+
{
34+
EnsureVolumeMountSource(volume.Source, volume.Destination);
35+
}
36+
}
37+
2838
var args = $"run -d --name {name}";
2939
if (network != null)
3040
args += $" --network {network}";
@@ -53,6 +63,54 @@ public bool Run(string image, string name, PortMapping[]? ports, Dictionary<stri
5363
return RunProcess("docker", args, out _, out _);
5464
}
5565

66+
/// <summary>
67+
/// Ensures that a volume mount source path exists correctly.
68+
/// Fixes the common Docker issue where mounting a non-existent file creates a directory instead.
69+
/// </summary>
70+
private void EnsureVolumeMountSource(string sourcePath, string destinationPath)
71+
{
72+
// Determine if destination looks like a file (has extension) or directory
73+
bool isFilePath = Path.HasExtension(destinationPath) && !destinationPath.EndsWith('/') && !destinationPath.EndsWith('\\');
74+
75+
// Check if path was incorrectly created as a directory when it should be a file
76+
if (isFilePath && Directory.Exists(sourcePath))
77+
{
78+
Console.WriteLine($"Mount path '{sourcePath}' exists as a directory instead of a file. Removing...");
79+
try
80+
{
81+
Directory.Delete(sourcePath, recursive: true);
82+
}
83+
catch (Exception ex)
84+
{
85+
throw new InvalidOperationException(
86+
$"Failed to remove directory '{sourcePath}': {ex.Message}",
87+
ex);
88+
}
89+
}
90+
91+
// Ensure parent directory exists
92+
var parentDir = Path.GetDirectoryName(sourcePath);
93+
if (!string.IsNullOrEmpty(parentDir) && !Directory.Exists(parentDir))
94+
{
95+
Directory.CreateDirectory(parentDir);
96+
}
97+
98+
// For file paths, ensure the file exists
99+
if (isFilePath && !File.Exists(sourcePath))
100+
{
101+
throw new FileNotFoundException(
102+
$"Mount source file does not exist: '{sourcePath}'. " +
103+
$"Please ensure the required config file exists before running this command.",
104+
sourcePath);
105+
}
106+
107+
// For directory paths, ensure directory exists
108+
if (!isFilePath && !Directory.Exists(sourcePath))
109+
{
110+
Directory.CreateDirectory(sourcePath);
111+
}
112+
}
113+
56114
public bool Exists(string name, bool checkRunning = true)
57115
{
58116
var filter = checkRunning ? "--filter \"status=running\"" : "";

cli/src/Vdk/Services/LocalDockerClient.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ public LocalDockerClient(Docker.DotNet.IDockerClient dockerClient)
1414

1515
public bool Run(string image, string name, PortMapping[]? ports, Dictionary<string, string>? envs, FileMapping[]? volumes, string[]? commands, string? network = null)
1616
{
17+
// Validate and fix volume mount sources before creating container
18+
// This prevents Docker from creating directories when files are expected
19+
if (volumes != null)
20+
{
21+
foreach (var volume in volumes)
22+
{
23+
EnsureVolumeMountSource(volume.Source, volume.Destination);
24+
}
25+
}
26+
1727
_dockerClient.Images.CreateImageAsync(
1828
new ImagesCreateParameters
1929
{
@@ -160,4 +170,52 @@ public bool CanConnect()
160170
return false;
161171
}
162172
}
173+
174+
/// <summary>
175+
/// Ensures that a volume mount source path exists correctly.
176+
/// Fixes the common Docker issue where mounting a non-existent file creates a directory instead.
177+
/// </summary>
178+
private void EnsureVolumeMountSource(string sourcePath, string destinationPath)
179+
{
180+
// Determine if destination looks like a file (has extension) or directory
181+
bool isFilePath = Path.HasExtension(destinationPath) && !destinationPath.EndsWith('/') && !destinationPath.EndsWith('\\');
182+
183+
// Check if path was incorrectly created as a directory when it should be a file
184+
if (isFilePath && Directory.Exists(sourcePath))
185+
{
186+
Console.WriteLine($"Certificate path '{sourcePath}' exists as a directory instead of a file. Removing...");
187+
try
188+
{
189+
Directory.Delete(sourcePath, recursive: true);
190+
}
191+
catch (Exception ex)
192+
{
193+
throw new InvalidOperationException(
194+
$"Failed to remove directory '{sourcePath}': {ex.Message}",
195+
ex);
196+
}
197+
}
198+
199+
// Ensure parent directory exists
200+
var parentDir = Path.GetDirectoryName(sourcePath);
201+
if (!string.IsNullOrEmpty(parentDir) && !Directory.Exists(parentDir))
202+
{
203+
Directory.CreateDirectory(parentDir);
204+
}
205+
206+
// For file paths, ensure the file exists
207+
if (isFilePath && !File.Exists(sourcePath))
208+
{
209+
throw new FileNotFoundException(
210+
$"Mount source file does not exist: '{sourcePath}'. " +
211+
$"Please ensure the required config file exists before running this command.",
212+
sourcePath);
213+
}
214+
215+
// For directory paths, ensure directory exists
216+
if (!isFilePath && !Directory.Exists(sourcePath))
217+
{
218+
Directory.CreateDirectory(sourcePath);
219+
}
220+
}
163221
}

0 commit comments

Comments
 (0)