2024-02-02 10:50:28 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
|
2024-07-02 11:39:44 +00:00
|
|
|
use App\Actions\Database\StartDatabase;
|
2024-02-02 10:50:28 +00:00
|
|
|
use App\Actions\Service\StartService;
|
|
|
|
use App\Http\Controllers\Controller;
|
2024-03-07 11:27:23 +00:00
|
|
|
use App\Models\ApplicationDeploymentQueue;
|
|
|
|
use App\Models\Server;
|
2024-02-02 10:50:28 +00:00
|
|
|
use App\Models\Tag;
|
|
|
|
use Illuminate\Http\Request;
|
2024-07-09 08:45:10 +00:00
|
|
|
use OpenApi\Attributes as OA;
|
2024-02-02 10:50:28 +00:00
|
|
|
use Visus\Cuid2\Cuid2;
|
|
|
|
|
2024-07-01 14:26:50 +00:00
|
|
|
class DeployController extends Controller
|
2024-02-02 10:50:28 +00:00
|
|
|
{
|
2024-07-02 10:15:58 +00:00
|
|
|
private function removeSensitiveData($deployment)
|
|
|
|
{
|
|
|
|
$token = auth()->user()->currentAccessToken();
|
|
|
|
if ($token->can('view:sensitive')) {
|
|
|
|
return serializeApiResponse($deployment);
|
|
|
|
}
|
|
|
|
|
|
|
|
$deployment->makeHidden([
|
|
|
|
'logs',
|
|
|
|
]);
|
|
|
|
|
|
|
|
return serializeApiResponse($deployment);
|
|
|
|
}
|
|
|
|
|
2024-07-09 08:45:10 +00:00
|
|
|
#[OA\Get(
|
|
|
|
summary: 'List',
|
|
|
|
description: 'List currently running deployments',
|
|
|
|
path: '/deployments',
|
|
|
|
security: [
|
|
|
|
['bearerAuth' => []],
|
|
|
|
],
|
|
|
|
tags: ['Deployments'],
|
|
|
|
responses: [
|
|
|
|
new OA\Response(
|
|
|
|
response: 200,
|
|
|
|
description: 'Get all currently running deployments.',
|
|
|
|
content: [
|
|
|
|
|
|
|
|
new OA\MediaType(
|
|
|
|
mediaType: 'application/json',
|
|
|
|
schema: new OA\Schema(
|
|
|
|
type: 'array',
|
|
|
|
items: new OA\Items(ref: '#/components/schemas/ApplicationDeploymentQueue'),
|
|
|
|
)
|
|
|
|
),
|
|
|
|
]),
|
|
|
|
new OA\Response(
|
|
|
|
response: 401,
|
|
|
|
ref: '#/components/responses/401',
|
|
|
|
),
|
|
|
|
new OA\Response(
|
|
|
|
response: 400,
|
|
|
|
ref: '#/components/responses/400',
|
|
|
|
),
|
|
|
|
]
|
|
|
|
)]
|
2024-03-11 08:42:02 +00:00
|
|
|
public function deployments(Request $request)
|
|
|
|
{
|
2024-07-01 14:26:50 +00:00
|
|
|
$teamId = getTeamIdFromToken();
|
2024-03-07 11:27:23 +00:00
|
|
|
if (is_null($teamId)) {
|
2024-07-01 14:26:50 +00:00
|
|
|
return invalidTokenResponse();
|
2024-03-07 11:27:23 +00:00
|
|
|
}
|
|
|
|
$servers = Server::whereTeamId($teamId)->get();
|
2024-07-09 08:45:10 +00:00
|
|
|
$deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get()->sortBy('id');
|
2024-07-04 11:45:06 +00:00
|
|
|
$deployments_per_server = $deployments_per_server->map(function ($deployment) {
|
|
|
|
return $this->removeSensitiveData($deployment);
|
|
|
|
});
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-07-04 11:45:06 +00:00
|
|
|
return response()->json($deployments_per_server);
|
2024-06-21 14:46:13 +00:00
|
|
|
}
|
|
|
|
|
2024-07-09 08:45:10 +00:00
|
|
|
#[OA\Get(
|
|
|
|
summary: 'Get',
|
|
|
|
description: 'Get deployment by UUID.',
|
|
|
|
path: '/deployments/{uuid}',
|
|
|
|
security: [
|
|
|
|
['bearerAuth' => []],
|
|
|
|
],
|
|
|
|
tags: ['Deployments'],
|
|
|
|
parameters: [
|
|
|
|
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'integer')),
|
|
|
|
],
|
|
|
|
responses: [
|
|
|
|
new OA\Response(
|
|
|
|
response: 200,
|
|
|
|
description: 'Get deployment by UUID.',
|
|
|
|
content: [
|
|
|
|
new OA\MediaType(
|
|
|
|
mediaType: 'application/json',
|
|
|
|
schema: new OA\Schema(
|
|
|
|
ref: '#/components/schemas/ApplicationDeploymentQueue',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
]),
|
|
|
|
new OA\Response(
|
|
|
|
response: 401,
|
|
|
|
ref: '#/components/responses/401',
|
|
|
|
),
|
|
|
|
new OA\Response(
|
|
|
|
response: 400,
|
|
|
|
ref: '#/components/responses/400',
|
|
|
|
),
|
|
|
|
new OA\Response(
|
|
|
|
response: 404,
|
|
|
|
ref: '#/components/responses/404',
|
|
|
|
),
|
|
|
|
]
|
|
|
|
)]
|
2024-06-21 14:46:13 +00:00
|
|
|
public function deployment_by_uuid(Request $request)
|
|
|
|
{
|
2024-07-01 14:26:50 +00:00
|
|
|
$teamId = getTeamIdFromToken();
|
2024-06-21 14:46:13 +00:00
|
|
|
if (is_null($teamId)) {
|
2024-07-01 14:26:50 +00:00
|
|
|
return invalidTokenResponse();
|
2024-06-21 14:46:13 +00:00
|
|
|
}
|
|
|
|
$uuid = $request->route('uuid');
|
|
|
|
if (! $uuid) {
|
2024-07-03 11:13:38 +00:00
|
|
|
return response()->json(['message' => 'UUID is required.'], 400);
|
2024-06-21 14:46:13 +00:00
|
|
|
}
|
2024-07-09 08:45:10 +00:00
|
|
|
$deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first();
|
2024-06-21 14:46:13 +00:00
|
|
|
if (! $deployment) {
|
2024-07-03 11:13:38 +00:00
|
|
|
return response()->json(['message' => 'Deployment not found.'], 404);
|
2024-06-21 14:46:13 +00:00
|
|
|
}
|
|
|
|
|
2024-07-03 11:13:38 +00:00
|
|
|
return response()->json($this->removeSensitiveData($deployment));
|
2024-03-07 11:27:23 +00:00
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-07-09 08:45:10 +00:00
|
|
|
#[OA\Get(
|
|
|
|
summary: 'Deploy',
|
|
|
|
description: 'Deploy by tag or uuid. `Post` request also accepted.',
|
|
|
|
path: '/deploy',
|
|
|
|
security: [
|
|
|
|
['bearerAuth' => []],
|
|
|
|
],
|
|
|
|
tags: ['Deployments'],
|
|
|
|
parameters: [
|
|
|
|
new OA\Parameter(name: 'tag', in: 'query', description: 'Tag name(s). Comma separated list is also accepted.', schema: new OA\Schema(type: 'string')),
|
|
|
|
new OA\Parameter(name: 'uuid', in: 'query', description: 'Resource UUID(s). Comma separated list is also accepted.', schema: new OA\Schema(type: 'string')),
|
|
|
|
new OA\Parameter(name: 'force', in: 'query', description: 'Force rebuild (without cache)', schema: new OA\Schema(type: 'boolean')),
|
|
|
|
],
|
|
|
|
|
|
|
|
responses: [
|
|
|
|
new OA\Response(
|
|
|
|
response: 200,
|
|
|
|
description: 'Get deployment(s) Uuid\'s',
|
|
|
|
content: [
|
|
|
|
new OA\MediaType(
|
|
|
|
mediaType: 'application/json',
|
|
|
|
schema: new OA\Schema(
|
|
|
|
type: 'object',
|
|
|
|
properties: [
|
|
|
|
'deployments' => new OA\Property(
|
|
|
|
property: 'deployments',
|
|
|
|
type: 'array',
|
|
|
|
items: new OA\Items(
|
|
|
|
type: 'object',
|
|
|
|
properties: [
|
|
|
|
'message' => ['type' => 'string'],
|
|
|
|
'resource_uuid' => ['type' => 'string'],
|
|
|
|
'deployment_uuid' => ['type' => 'string'],
|
|
|
|
]
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
),
|
|
|
|
]),
|
|
|
|
new OA\Response(
|
|
|
|
response: 401,
|
|
|
|
ref: '#/components/responses/401',
|
|
|
|
),
|
|
|
|
new OA\Response(
|
|
|
|
response: 400,
|
|
|
|
ref: '#/components/responses/400',
|
|
|
|
),
|
|
|
|
|
|
|
|
]
|
|
|
|
)]
|
2024-02-02 10:50:28 +00:00
|
|
|
public function deploy(Request $request)
|
|
|
|
{
|
2024-07-01 14:26:50 +00:00
|
|
|
$teamId = getTeamIdFromToken();
|
2024-02-02 10:50:28 +00:00
|
|
|
$uuids = $request->query->get('uuid');
|
|
|
|
$tags = $request->query->get('tag');
|
|
|
|
$force = $request->query->get('force') ?? false;
|
|
|
|
|
|
|
|
if ($uuids && $tags) {
|
2024-07-09 11:59:54 +00:00
|
|
|
return response()->json(['message' => 'You can only use uuid or tag, not both.'], 400);
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
|
|
|
if (is_null($teamId)) {
|
2024-07-01 14:26:50 +00:00
|
|
|
return invalidTokenResponse();
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
|
|
|
if ($tags) {
|
|
|
|
return $this->by_tags($tags, $teamId, $force);
|
2024-06-10 20:43:34 +00:00
|
|
|
} elseif ($uuids) {
|
2024-02-02 10:50:28 +00:00
|
|
|
return $this->by_uuids($uuids, $teamId, $force);
|
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-07-09 11:59:54 +00:00
|
|
|
return response()->json(['message' => 'You must provide uuid or tag.'], 400);
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-02-02 10:50:28 +00:00
|
|
|
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
|
|
|
{
|
|
|
|
$uuids = explode(',', $uuid);
|
|
|
|
$uuids = collect(array_filter($uuids));
|
|
|
|
|
|
|
|
if (count($uuids) === 0) {
|
2024-07-09 11:59:54 +00:00
|
|
|
return response()->json(['message' => 'No UUIDs provided.'], 400);
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
2024-03-07 11:22:18 +00:00
|
|
|
$deployments = collect();
|
|
|
|
$payload = collect();
|
2024-02-02 10:50:28 +00:00
|
|
|
foreach ($uuids as $uuid) {
|
|
|
|
$resource = getResourceByUuid($uuid, $teamId);
|
|
|
|
if ($resource) {
|
2024-03-07 11:22:18 +00:00
|
|
|
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
|
|
|
if ($deployment_uuid) {
|
2024-07-03 11:13:38 +00:00
|
|
|
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
2024-03-11 08:42:02 +00:00
|
|
|
} else {
|
2024-07-03 11:13:38 +00:00
|
|
|
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]);
|
2024-03-07 11:22:18 +00:00
|
|
|
}
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
|
|
|
}
|
2024-03-11 08:42:02 +00:00
|
|
|
if ($deployments->count() > 0) {
|
|
|
|
$payload->put('deployments', $deployments->toArray());
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-07-03 11:13:38 +00:00
|
|
|
return response()->json(serializeApiResponse($payload->toArray()));
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-07-09 11:59:54 +00:00
|
|
|
return response()->json(['message' => 'No resources found.'], 404);
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-02-02 10:50:28 +00:00
|
|
|
public function by_tags(string $tags, int $team_id, bool $force = false)
|
|
|
|
{
|
|
|
|
$tags = explode(',', $tags);
|
|
|
|
$tags = collect(array_filter($tags));
|
|
|
|
|
|
|
|
if (count($tags) === 0) {
|
2024-07-09 11:59:54 +00:00
|
|
|
return response()->json(['message' => 'No TAGs provided.'], 400);
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
|
|
|
$message = collect([]);
|
2024-03-07 11:22:18 +00:00
|
|
|
$deployments = collect();
|
|
|
|
$payload = collect();
|
2024-02-02 10:50:28 +00:00
|
|
|
foreach ($tags as $tag) {
|
|
|
|
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
|
2024-06-10 20:43:34 +00:00
|
|
|
if (! $found_tag) {
|
2024-03-07 11:35:38 +00:00
|
|
|
// $message->push("Tag {$tag} not found.");
|
2024-02-02 10:50:28 +00:00
|
|
|
continue;
|
|
|
|
}
|
2024-02-06 06:21:06 +00:00
|
|
|
$applications = $found_tag->applications()->get();
|
|
|
|
$services = $found_tag->services()->get();
|
2024-02-03 11:39:07 +00:00
|
|
|
if ($applications->count() === 0 && $services->count() === 0) {
|
2024-02-02 10:50:28 +00:00
|
|
|
$message->push("No resources found for tag {$tag}.");
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-02-02 10:50:28 +00:00
|
|
|
continue;
|
|
|
|
}
|
2024-02-03 11:39:07 +00:00
|
|
|
foreach ($applications as $resource) {
|
2024-03-07 11:22:18 +00:00
|
|
|
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
|
|
|
if ($deployment_uuid) {
|
|
|
|
$deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
|
|
|
}
|
2024-02-03 11:39:07 +00:00
|
|
|
$message = $message->merge($return_message);
|
|
|
|
}
|
|
|
|
foreach ($services as $resource) {
|
2024-03-07 11:22:18 +00:00
|
|
|
['message' => $return_message] = $this->deploy_resource($resource, $force);
|
2024-02-02 10:50:28 +00:00
|
|
|
$message = $message->merge($return_message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($message->count() > 0) {
|
2024-03-07 11:22:18 +00:00
|
|
|
$payload->put('message', $message->toArray());
|
|
|
|
if ($deployments->count() > 0) {
|
|
|
|
$payload->put('details', $deployments->toArray());
|
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-07-03 11:13:38 +00:00
|
|
|
return response()->json(serializeApiResponse($payload->toArray()));
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
|
|
|
|
2024-07-09 11:59:54 +00:00
|
|
|
return response()->json(['message' => 'No resources found with this tag.'], 404);
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-03-07 11:22:18 +00:00
|
|
|
public function deploy_resource($resource, bool $force = false): array
|
2024-02-02 10:50:28 +00:00
|
|
|
{
|
2024-03-11 08:42:02 +00:00
|
|
|
$message = null;
|
|
|
|
$deployment_uuid = null;
|
2024-02-06 06:19:11 +00:00
|
|
|
if (gettype($resource) !== 'object') {
|
2024-07-03 11:13:38 +00:00
|
|
|
return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
|
2024-02-06 06:19:11 +00:00
|
|
|
}
|
2024-07-02 11:39:44 +00:00
|
|
|
switch ($resource?->getMorphClass()) {
|
|
|
|
case 'App\Models\Application':
|
|
|
|
$deployment_uuid = new Cuid2(7);
|
|
|
|
queue_application_deployment(
|
|
|
|
application: $resource,
|
|
|
|
deployment_uuid: $deployment_uuid,
|
|
|
|
force_rebuild: $force,
|
|
|
|
);
|
|
|
|
$message = "Application {$resource->name} deployment queued.";
|
|
|
|
break;
|
|
|
|
case 'App\Models\Service':
|
|
|
|
StartService::run($resource);
|
|
|
|
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Database resource
|
|
|
|
StartDatabase::dispatch($resource);
|
|
|
|
$resource->update([
|
|
|
|
'started_at' => now(),
|
|
|
|
]);
|
|
|
|
$message = "Database {$resource->name} started.";
|
|
|
|
break;
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-07-03 11:13:38 +00:00
|
|
|
return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
|
2024-02-02 10:50:28 +00:00
|
|
|
}
|
|
|
|
}
|