mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
chore: add registry auth to docker image apps
This commit is contained in:
parent
013bea0495
commit
b57a439b76
4 changed files with 116 additions and 25 deletions
|
|
@ -381,17 +381,35 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
|
||||
private function deploy_dockerimage_buildpack()
|
||||
{
|
||||
$this->dockerImage = $this->application->docker_registry_image_name;
|
||||
if (str($this->application->docker_registry_image_tag)->isEmpty()) {
|
||||
$this->dockerImageTag = 'latest';
|
||||
} else {
|
||||
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||
try {
|
||||
$didLogin = $this->handleRegistryAuth();
|
||||
|
||||
// Pull the image
|
||||
$this->execute_remote_command([
|
||||
"docker pull {$this->application->image}",
|
||||
]);
|
||||
|
||||
// Logout if we logged in
|
||||
if ($didLogin) {
|
||||
$this->application_deployment_queue->addLogEntry('Logging out from registry...');
|
||||
$this->execute_remote_command([
|
||||
'docker logout',
|
||||
'hidden' => true
|
||||
]);
|
||||
}
|
||||
|
||||
// Continue with the rest of the deployment...
|
||||
} catch (Exception $e) {
|
||||
// Make sure to logout even if pull fails
|
||||
if ($didLogin ?? false) {
|
||||
$this->execute_remote_command([
|
||||
'docker logout',
|
||||
'hidden' => true
|
||||
]);
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry('Deployment error: ' . $e->getMessage(), 'stderr');
|
||||
throw $e;
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.");
|
||||
$this->generate_image_names();
|
||||
$this->prepare_builder_image();
|
||||
$this->generate_compose_file();
|
||||
$this->rolling_update();
|
||||
}
|
||||
|
||||
private function deploy_docker_compose_buildpack()
|
||||
|
|
@ -2444,4 +2462,29 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handleRegistryAuth()
|
||||
{
|
||||
if ($this->application->registry_username && $this->application->registry_token) {
|
||||
try {
|
||||
$username = escapeshellarg($this->application->registry_username);
|
||||
$token = escapeshellarg(decrypt($this->application->registry_token));
|
||||
|
||||
$this->application_deployment_queue->addLogEntry('Attempting to log into registry...');
|
||||
|
||||
$command = "echo {$token} | docker login -u {$username} --password-stdin > /dev/null";
|
||||
|
||||
$this->execute_remote_command([
|
||||
$command,
|
||||
'hidden' => true,
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$this->application_deployment_queue->addLogEntry('Registry authentication error: ' . $e->getMessage(), 'stderr');
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,17 @@ use Visus\Cuid2\Cuid2;
|
|||
class DockerImage extends Component
|
||||
{
|
||||
public string $dockerImage = '';
|
||||
|
||||
public ?string $registryUsername = null;
|
||||
public ?string $registryToken = null;
|
||||
public array $parameters;
|
||||
|
||||
public array $query;
|
||||
|
||||
protected $rules = [
|
||||
'dockerImage' => 'required|string',
|
||||
'registryUsername' => 'nullable|string',
|
||||
'registryToken' => 'nullable|string',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
|
|
@ -27,25 +33,30 @@ class DockerImage extends Component
|
|||
{
|
||||
$this->validate([
|
||||
'dockerImage' => 'required',
|
||||
'registryUsername' => 'required_with:registryToken',
|
||||
'registryToken' => 'required_with:registryUsername',
|
||||
]);
|
||||
|
||||
$image = str($this->dockerImage)->before(':');
|
||||
if (str($this->dockerImage)->contains(':')) {
|
||||
$tag = str($this->dockerImage)->after(':');
|
||||
} else {
|
||||
$tag = 'latest';
|
||||
}
|
||||
$tag = str($this->dockerImage)->contains(':') ?
|
||||
str($this->dockerImage)->after(':') :
|
||||
'latest';
|
||||
|
||||
$destination_uuid = $this->query['destination'];
|
||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||
if (! $destination) {
|
||||
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||
}
|
||||
if (! $destination) {
|
||||
throw new \Exception('Destination not found. What?!');
|
||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first()
|
||||
?? SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||
|
||||
if (!$destination) {
|
||||
throw new \Exception('Destination not found.');
|
||||
}
|
||||
$destination_class = $destination->getMorphClass();
|
||||
|
||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||
$environment = $project->load(['environments'])
|
||||
->environments
|
||||
->where('name', $this->parameters['environment_name'])
|
||||
->first();
|
||||
|
||||
$application = Application::create([
|
||||
'name' => 'docker-image-'.new Cuid2,
|
||||
'repository_project_id' => 0,
|
||||
|
|
@ -59,6 +70,8 @@ class DockerImage extends Component
|
|||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination_class,
|
||||
'health_check_enabled' => false,
|
||||
'registry_username' => $this->registryUsername,
|
||||
'registry_token' => $this->registryToken ? encrypt($this->registryToken) : null,
|
||||
]);
|
||||
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->string('registry_username')->nullable();
|
||||
$table->text('registry_token')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->dropColumn('registry_username');
|
||||
$table->dropColumn('registry_token');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -6,6 +6,18 @@
|
|||
<h2>Docker Image</h2>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
</div>
|
||||
<x-forms.input rows="20" id="dockerImage" placeholder="nginx:latest"></x-forms.textarea>
|
||||
<x-forms.input required id="dockerImage" label="Image" placeholder="nginx:latest" />
|
||||
|
||||
<h3 class="pt-4">Registry Authentication</h3>
|
||||
<div class="flex flex-col gap-4">
|
||||
<x-forms.input id="registryUsername" required="registryToken" label="Registry Username"
|
||||
wire:model="registryUsername" placeholder="Username for private registry"
|
||||
helper="Leave empty for public images or server credentials" />
|
||||
|
||||
<x-forms.input type="password" required="registryUsername" id="registryToken"
|
||||
label="Registry Token/Password" wire:model="registryToken"
|
||||
placeholder="Token or password for private registry"
|
||||
helper="Leave empty for public images or server credentials" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue