mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-17 08:14:25 +00:00
feat(docker): enhance Docker image handling with new validation and parsing logic
- Refactored DockerImage component to use separate properties for image name, tag, and SHA256 digest. - Introduced DockerImageFormat validation rule to enforce correct image format. - Updated DockerImageParser to handle new parsing logic for image tags and SHA256 hashes. - Enhanced UI to separate input fields for image name, tag, and SHA256 digest, improving user experience. - Added comprehensive tests for DockerImageParser to ensure accurate parsing and validation of image formats.
This commit is contained in:
1 parent
0e02eff4a1
commit
590de8ce37
7 files changed
+209
-133
No files matched your search
@@ -12,7 +12,11 @@ use Visus\Cuid2\Cuid2;
|
||||
|
||||
class DockerImage extends Component
|
||||
{
|
||||
public string $dockerImage = '';
|
||||
public string $imageName = '';
|
||||
|
||||
public string $imageTag = '';
|
||||
|
||||
public string $imageSha256 = '';
|
||||
|
||||
public array $parameters;
|
||||
|
||||
@@ -27,11 +31,30 @@ class DockerImage extends Component
|
||||
public function submit()
|
||||
{
|
||||
$this->validate([
|
||||
'dockerImage' => 'required',
|
||||
'imageName' => ['required', 'string'],
|
||||
'imageTag' => ['nullable', 'string', 'regex:/^[a-z0-9][a-z0-9._-]*$/i'],
|
||||
'imageSha256' => ['nullable', 'string', 'regex:/^[a-f0-9]{64}$/i'],
|
||||
]);
|
||||
|
||||
// Validate that either tag or sha256 is provided, but not both
|
||||
if ($this->imageTag && $this->imageSha256) {
|
||||
$this->addError('imageTag', 'Provide either a tag or SHA256 digest, not both.');
|
||||
$this->addError('imageSha256', 'Provide either a tag or SHA256 digest, not both.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the full Docker image string
|
||||
if ($this->imageSha256) {
|
||||
$dockerImage = $this->imageName.'@sha256:'.$this->imageSha256;
|
||||
} elseif ($this->imageTag) {
|
||||
$dockerImage = $this->imageName.':'.$this->imageTag;
|
||||
} else {
|
||||
$dockerImage = $this->imageName.':latest';
|
||||
}
|
||||
|
||||
$parser = new DockerImageParser;
|
||||
$parser->parse($this->dockerImage);
|
||||
$parser->parse($dockerImage);
|
||||
|
||||
$destination_uuid = $this->query['destination'];
|
||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
class DockerImageFormat implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
// Check if the value contains ":sha256:" or ":sha" which is incorrect format
|
||||
if (preg_match('/:sha256?:/i', $value)) {
|
||||
$fail('The :attribute must use @ before sha256 digest (e.g., image@sha256:hash, not image:sha256:hash).');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Valid formats:
|
||||
// 1. image:tag (e.g., nginx:latest)
|
||||
// 2. registry/image:tag (e.g., ghcr.io/user/app:v1.2.3)
|
||||
// 3. image@sha256:hash (e.g., nginx@sha256:abc123...)
|
||||
// 4. registry/image@sha256:hash
|
||||
// 5. registry:port/image:tag (e.g., localhost:5000/app:latest)
|
||||
|
||||
$pattern = '/^
|
||||
(?:[a-z0-9]+(?:[._-][a-z0-9]+)*(?::[0-9]+)?\/)? # Optional registry with optional port
|
||||
[a-z0-9]+(?:[._\/-][a-z0-9]+)* # Image name (required)
|
||||
(?::[a-z0-9][a-z0-9._-]*|@sha256:[a-f0-9]{64})? # Optional :tag or @sha256:hash
|
||||
$/ix';
|
||||
|
||||
if (! preg_match($pattern, $value)) {
|
||||
$fail('The :attribute format is invalid. Use image:tag or image@sha256:hash format.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,22 +14,29 @@ class DockerImageParser
|
||||
|
||||
public function parse(string $imageString): self
|
||||
{
|
||||
// First split by : to handle the tag, but be careful with registry ports
|
||||
$lastColon = strrpos($imageString, ':');
|
||||
$hasSlash = str_contains($imageString, '/');
|
||||
|
||||
// If the last colon appears after the last slash, it's a tag
|
||||
// Otherwise it might be a port in the registry URL
|
||||
if ($lastColon !== false && (! $hasSlash || $lastColon > strrpos($imageString, '/'))) {
|
||||
$mainPart = substr($imageString, 0, $lastColon);
|
||||
$this->tag = substr($imageString, $lastColon + 1);
|
||||
|
||||
// Check if the tag is a SHA256 hash
|
||||
$this->isImageHash = $this->isSha256Hash($this->tag);
|
||||
// Check for @sha256: format first (e.g., nginx@sha256:abc123...)
|
||||
if (preg_match('/^(.+)@sha256:([a-f0-9]{64})$/i', $imageString, $matches)) {
|
||||
$mainPart = $matches[1];
|
||||
$this->tag = $matches[2];
|
||||
$this->isImageHash = true;
|
||||
} else {
|
||||
$mainPart = $imageString;
|
||||
$this->tag = 'latest';
|
||||
$this->isImageHash = false;
|
||||
// Split by : to handle the tag, but be careful with registry ports
|
||||
$lastColon = strrpos($imageString, ':');
|
||||
$hasSlash = str_contains($imageString, '/');
|
||||
|
||||
// If the last colon appears after the last slash, it's a tag
|
||||
// Otherwise it might be a port in the registry URL
|
||||
if ($lastColon !== false && (! $hasSlash || $lastColon > strrpos($imageString, '/'))) {
|
||||
$mainPart = substr($imageString, 0, $lastColon);
|
||||
$this->tag = substr($imageString, $lastColon + 1);
|
||||
|
||||
// Check if the tag is a SHA256 hash
|
||||
$this->isImageHash = $this->isSha256Hash($this->tag);
|
||||
} else {
|
||||
$mainPart = $imageString;
|
||||
$this->tag = 'latest';
|
||||
$this->isImageHash = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Split the main part by / to handle registry and image name
|
||||
|
||||
Reference in new issue
Block a user