-
Notifications
You must be signed in to change notification settings - Fork 0
Hotfix/certs #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hotfix/certs #53
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,67 @@ public void CreateRegistry() | |||||||||||||||||||||||||||||||||||||||||||||||
| var configFile = new FileInfo(Path.Combine("ConfigMounts", "zot-config.json")); | ||||||||||||||||||||||||||||||||||||||||||||||||
| var imagesDir = new DirectoryInfo("images"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Ensure ConfigMounts directory exists | ||||||||||||||||||||||||||||||||||||||||||||||||
| var configMountsDir = configFile.Directory; | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (configMountsDir != null && !configMountsDir.Exists) | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| configMountsDir.Create(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Fix: Check if config file was incorrectly created as a directory by Docker | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (Directory.Exists(configFile.FullName)) | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| console.WriteLine($"Config path '{configFile.FullName}' exists as a directory instead of a file. Removing..."); | ||||||||||||||||||||||||||||||||||||||||||||||||
| Directory.Delete(configFile.FullName, recursive: true); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
| Directory.Delete(configFile.FullName, recursive: true); | |
| try | |
| { | |
| Directory.Delete(configFile.FullName, recursive: true); | |
| } | |
| catch (UnauthorizedAccessException ex) | |
| { | |
| console.WriteLine($"Failed to remove directory at '{configFile.FullName}' due to insufficient permissions: {ex.Message}"); | |
| console.WriteLine("Please remove this directory manually (or re-run with elevated permissions) and try again."); | |
| throw; | |
| } | |
| catch (IOException ex) | |
| { | |
| console.WriteLine($"Failed to remove directory at '{configFile.FullName}' because it is in use or locked: {ex.Message}"); | |
| console.WriteLine("Please ensure no other process is using this path, remove it manually, and try again."); | |
| throw; | |
| } | |
| catch (Exception ex) | |
| { | |
| console.WriteLine($"Unexpected error while removing directory at '{configFile.FullName}': {ex.Message}"); | |
| console.WriteLine("Please remove this directory manually and try again."); | |
| throw; | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,16 @@ internal static bool RunProcess(string fileName, string arguments, out string st | |
|
|
||
| public bool Run(string image, string name, PortMapping[]? ports, Dictionary<string, string>? envs, FileMapping[]? volumes, string[]? commands, string? network = null) | ||
| { | ||
| // Validate and fix volume mount sources before creating container | ||
| // This prevents Docker from creating directories when files are expected | ||
| if (volumes != null) | ||
| { | ||
| foreach (var volume in volumes) | ||
| { | ||
| EnsureVolumeMountSource(volume.Source, volume.Destination); | ||
| } | ||
|
Comment on lines
+30
to
+35
|
||
| } | ||
|
|
||
| var args = $"run -d --name {name}"; | ||
| if (network != null) | ||
| args += $" --network {network}"; | ||
|
|
@@ -53,6 +63,54 @@ public bool Run(string image, string name, PortMapping[]? ports, Dictionary<stri | |
| return RunProcess("docker", args, out _, out _); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Ensures that a volume mount source path exists correctly. | ||
| /// Fixes the common Docker issue where mounting a non-existent file creates a directory instead. | ||
| /// </summary> | ||
| private void EnsureVolumeMountSource(string sourcePath, string destinationPath) | ||
| { | ||
| // Determine if destination looks like a file (has extension) or directory | ||
| bool isFilePath = Path.HasExtension(destinationPath) && !destinationPath.EndsWith('/') && !destinationPath.EndsWith('\\'); | ||
|
|
||
|
Comment on lines
+72
to
+74
|
||
| // Check if path was incorrectly created as a directory when it should be a file | ||
| if (isFilePath && Directory.Exists(sourcePath)) | ||
| { | ||
| Console.WriteLine($"Mount path '{sourcePath}' exists as a directory instead of a file. Removing..."); | ||
| try | ||
| { | ||
| Directory.Delete(sourcePath, recursive: true); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| throw new InvalidOperationException( | ||
| $"Failed to remove directory '{sourcePath}': {ex.Message}", | ||
| ex); | ||
| } | ||
| } | ||
|
|
||
| // Ensure parent directory exists | ||
| var parentDir = Path.GetDirectoryName(sourcePath); | ||
| if (!string.IsNullOrEmpty(parentDir) && !Directory.Exists(parentDir)) | ||
| { | ||
| Directory.CreateDirectory(parentDir); | ||
| } | ||
|
|
||
| // For file paths, ensure the file exists | ||
| if (isFilePath && !File.Exists(sourcePath)) | ||
| { | ||
| throw new FileNotFoundException( | ||
| $"Mount source file does not exist: '{sourcePath}'. " + | ||
| $"Please ensure the required config file exists before running this command.", | ||
| sourcePath); | ||
| } | ||
|
|
||
| // For directory paths, ensure directory exists | ||
| if (!isFilePath && !Directory.Exists(sourcePath)) | ||
| { | ||
| Directory.CreateDirectory(sourcePath); | ||
| } | ||
| } | ||
|
|
||
| public bool Exists(string name, bool checkRunning = true) | ||
| { | ||
| var filter = checkRunning ? "--filter \"status=running\"" : ""; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,16 @@ public LocalDockerClient(Docker.DotNet.IDockerClient dockerClient) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public bool Run(string image, string name, PortMapping[]? ports, Dictionary<string, string>? envs, FileMapping[]? volumes, string[]? commands, string? network = null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate and fix volume mount sources before creating container | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This prevents Docker from creating directories when files are expected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (volumes != null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var volume in volumes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EnsureVolumeMountSource(volume.Source, volume.Destination); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _dockerClient.Images.CreateImageAsync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new ImagesCreateParameters | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -160,4 +170,52 @@ public bool CanConnect() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Ensures that a volume mount source path exists correctly. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Fixes the common Docker issue where mounting a non-existent file creates a directory instead. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void EnsureVolumeMountSource(string sourcePath, string destinationPath) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Determine if destination looks like a file (has extension) or directory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool isFilePath = Path.HasExtension(destinationPath) && !destinationPath.EndsWith('/') && !destinationPath.EndsWith('\\'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if path was incorrectly created as a directory when it should be a file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isFilePath && Directory.Exists(sourcePath)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Console.WriteLine($"Certificate path '{sourcePath}' exists as a directory instead of a file. Removing..."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Console.WriteLine($"Certificate path '{sourcePath}' exists as a directory instead of a file. Removing..."); | |
| Console.WriteLine($"Mount source path '{sourcePath}' exists as a directory instead of a file. Removing..."); |
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file-vs-directory detection uses Path.HasExtension(destinationPath) which misclassifies valid file mounts without extensions (e.g., /var/run/docker.sock used elsewhere) as directories. In the missing-source case this code may create a directory at a path that should be a file/socket, and File.Exists may also be false for non-regular files. Consider determining type from sourcePath when it exists (file vs directory), and when it doesn’t exist require an explicit indicator (or treat as file unless destination ends with a path separator).
| // Determine if destination looks like a file (has extension) or directory | |
| bool isFilePath = Path.HasExtension(destinationPath) && !destinationPath.EndsWith('/') && !destinationPath.EndsWith('\\'); | |
| // Check if path was incorrectly created as a directory when it should be a file | |
| if (isFilePath && Directory.Exists(sourcePath)) | |
| { | |
| Console.WriteLine($"Certificate path '{sourcePath}' exists as a directory instead of a file. Removing..."); | |
| try | |
| { | |
| Directory.Delete(sourcePath, recursive: true); | |
| } | |
| catch (Exception ex) | |
| { | |
| throw new InvalidOperationException( | |
| $"Failed to remove directory '{sourcePath}': {ex.Message}", | |
| ex); | |
| } | |
| } | |
| // Determine whether this mount should be treated as a file or directory. | |
| // Prefer the actual type of sourcePath when it exists; otherwise infer from destinationPath. | |
| bool sourceIsFile = File.Exists(sourcePath); | |
| bool sourceIsDirectory = !sourceIsFile && Directory.Exists(sourcePath); | |
| bool isFilePath; | |
| if (sourceIsFile) | |
| { | |
| // Existing source is a file | |
| isFilePath = true; | |
| } | |
| else if (sourceIsDirectory) | |
| { | |
| // Existing source is a directory | |
| isFilePath = false; | |
| } | |
| else | |
| { | |
| // Source does not exist yet, infer from destination: | |
| // treat as a directory only if destination ends with a directory separator. | |
| bool endsWithSeparator = | |
| destinationPath.EndsWith(Path.DirectorySeparatorChar.ToString()) || | |
| destinationPath.EndsWith(Path.AltDirectorySeparatorChar.ToString()); | |
| isFilePath = !endsWithSeparator; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WaitForExit(timeout)is used without checking completion; ifrm/sudohangs, the process can remain running after the timeout. Check the return value and kill the process on timeout to avoid leaving orphaned processes.